// monorepo/cloud/maplefile-backend/internal/maplefile/repo/filemetadata/create.go
package filemetadata

import (
	"context"
	"fmt"
	"time"

	"github.com/gocql/gocql"
	"go.uber.org/zap"

	dom_file "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/file"
)

func (impl *fileMetadataRepositoryImpl) Create(file *dom_file.File) error {
	if file == nil {
		return fmt.Errorf("file cannot be nil")
	}

	if !impl.isValidUUID(file.ID) {
		return fmt.Errorf("file ID is required")
	}

	if !impl.isValidUUID(file.CollectionID) {
		return fmt.Errorf("collection ID is required")
	}

	if !impl.isValidUUID(file.OwnerID) {
		return fmt.Errorf("owner ID is required")
	}

	// Set creation timestamp if not set
	if file.CreatedAt.IsZero() {
		file.CreatedAt = time.Now()
	}

	if file.ModifiedAt.IsZero() {
		file.ModifiedAt = file.CreatedAt
	}

	// Ensure state is set
	if file.State == "" {
		file.State = dom_file.FileStateActive
	}

	// Serialize encrypted file key
	encryptedKeyJSON, err := impl.serializeEncryptedFileKey(file.EncryptedFileKey)
	if err != nil {
		return fmt.Errorf("failed to serialize encrypted file key: %w", err)
	}

	// Serialize tags
	tagsJSON, err := impl.serializeTags(file.Tags)
	if err != nil {
		return fmt.Errorf("failed to serialize tags: %w", err)
	}

	batch := impl.Session.NewBatch(gocql.LoggedBatch)

	// 1. Insert into main table
	batch.Query(`INSERT INTO maplefile.files_by_id
		(id, collection_id, owner_id, encrypted_metadata, encrypted_file_key, encryption_version,
		 encrypted_hash, encrypted_file_object_key, encrypted_file_size_in_bytes,
		 encrypted_thumbnail_object_key, encrypted_thumbnail_size_in_bytes, tags,
		 created_at, created_by_user_id, modified_at, modified_by_user_id, version,
		 state, tombstone_version, tombstone_expiry)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
		file.ID, file.CollectionID, file.OwnerID, file.EncryptedMetadata, encryptedKeyJSON,
		file.EncryptionVersion, file.EncryptedHash, file.EncryptedFileObjectKey,
		file.EncryptedFileSizeInBytes, file.EncryptedThumbnailObjectKey,
		file.EncryptedThumbnailSizeInBytes, tagsJSON, file.CreatedAt, file.CreatedByUserID,
		file.ModifiedAt, file.ModifiedByUserID, file.Version, file.State,
		file.TombstoneVersion, file.TombstoneExpiry)

	// 2. Insert into collection table
	batch.Query(`INSERT INTO maplefile.files_by_collection
		(collection_id, modified_at, id, owner_id, encrypted_metadata, encrypted_file_key,
		 encryption_version, encrypted_hash, encrypted_file_object_key, encrypted_file_size_in_bytes,
		 encrypted_thumbnail_object_key, encrypted_thumbnail_size_in_bytes,
		 created_at, created_by_user_id, modified_by_user_id, version,
		 state, tombstone_version, tombstone_expiry)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
		file.CollectionID, file.ModifiedAt, file.ID, file.OwnerID, file.EncryptedMetadata,
		encryptedKeyJSON, file.EncryptionVersion, file.EncryptedHash, file.EncryptedFileObjectKey,
		file.EncryptedFileSizeInBytes, file.EncryptedThumbnailObjectKey,
		file.EncryptedThumbnailSizeInBytes, file.CreatedAt, file.CreatedByUserID,
		file.ModifiedByUserID, file.Version, file.State, file.TombstoneVersion, file.TombstoneExpiry)

	// 3. Insert into owner table
	batch.Query(`INSERT INTO maplefile.files_by_owner
		(owner_id, modified_at, id, collection_id, encrypted_metadata, encrypted_file_key,
		 encryption_version, encrypted_hash, encrypted_file_object_key, encrypted_file_size_in_bytes,
		 encrypted_thumbnail_object_key, encrypted_thumbnail_size_in_bytes,
		 created_at, created_by_user_id, modified_by_user_id, version,
		 state, tombstone_version, tombstone_expiry)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
		file.OwnerID, file.ModifiedAt, file.ID, file.CollectionID, file.EncryptedMetadata,
		encryptedKeyJSON, file.EncryptionVersion, file.EncryptedHash, file.EncryptedFileObjectKey,
		file.EncryptedFileSizeInBytes, file.EncryptedThumbnailObjectKey,
		file.EncryptedThumbnailSizeInBytes, file.CreatedAt, file.CreatedByUserID,
		file.ModifiedByUserID, file.Version, file.State, file.TombstoneVersion, file.TombstoneExpiry)

	// 4. Insert into created_by table
	batch.Query(`INSERT INTO maplefile.files_by_creator
		(created_by_user_id, created_at, id, collection_id, owner_id, encrypted_metadata,
		 encrypted_file_key, encryption_version, encrypted_hash, encrypted_file_object_key,
		 encrypted_file_size_in_bytes, encrypted_thumbnail_object_key, encrypted_thumbnail_size_in_bytes,
		 modified_at, modified_by_user_id, version, state, tombstone_version, tombstone_expiry)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
		file.CreatedByUserID, file.CreatedAt, file.ID, file.CollectionID, file.OwnerID,
		file.EncryptedMetadata, encryptedKeyJSON, file.EncryptionVersion, file.EncryptedHash,
		file.EncryptedFileObjectKey, file.EncryptedFileSizeInBytes, file.EncryptedThumbnailObjectKey,
		file.EncryptedThumbnailSizeInBytes, file.ModifiedAt, file.ModifiedByUserID, file.Version,
		file.State, file.TombstoneVersion, file.TombstoneExpiry)

	// 5. Insert into user sync table (for owner and any collection members)
	batch.Query(`INSERT INTO maplefile.files_by_user
		(user_id, modified_at, id, collection_id, owner_id, encrypted_metadata,
		 encrypted_file_key, encryption_version, encrypted_hash, encrypted_file_object_key,
		 encrypted_file_size_in_bytes, encrypted_thumbnail_object_key, encrypted_thumbnail_size_in_bytes,
		 tags, created_at, created_by_user_id, modified_by_user_id, version,
		 state, tombstone_version, tombstone_expiry)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
		file.OwnerID, file.ModifiedAt, file.ID, file.CollectionID, file.OwnerID,
		file.EncryptedMetadata, encryptedKeyJSON, file.EncryptionVersion, file.EncryptedHash,
		file.EncryptedFileObjectKey, file.EncryptedFileSizeInBytes, file.EncryptedThumbnailObjectKey,
		file.EncryptedThumbnailSizeInBytes, tagsJSON, file.CreatedAt, file.CreatedByUserID,
		file.ModifiedByUserID, file.Version, file.State, file.TombstoneVersion, file.TombstoneExpiry)

	// 6. Insert into denormalized files_by_tag_id table for each tag
	for _, tag := range file.Tags {
		batch.Query(`INSERT INTO maplefile.files_by_tag_id
			(tag_id, file_id, collection_id, owner_id, encrypted_metadata, encrypted_file_key,
			 encryption_version, encrypted_hash, encrypted_file_object_key,
			 encrypted_file_size_in_bytes, encrypted_thumbnail_object_key,
			 encrypted_thumbnail_size_in_bytes, tag_ids, created_at, created_by_user_id,
			 modified_at, modified_by_user_id, version, state, tombstone_version, tombstone_expiry,
			 created_from_ip_address, modified_from_ip_address, ip_anonymized_at)
			VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
			tag.ID, file.ID, file.CollectionID, file.OwnerID, file.EncryptedMetadata,
			encryptedKeyJSON, file.EncryptionVersion, file.EncryptedHash,
			file.EncryptedFileObjectKey, file.EncryptedFileSizeInBytes,
			file.EncryptedThumbnailObjectKey, file.EncryptedThumbnailSizeInBytes,
			tagsJSON, file.CreatedAt, file.CreatedByUserID, file.ModifiedAt,
			file.ModifiedByUserID, file.Version, file.State, file.TombstoneVersion,
			file.TombstoneExpiry,
			nil, nil, nil) // IP tracking fields not yet in domain model
	}

	// Execute batch
	if err := impl.Session.ExecuteBatch(batch); err != nil {
		impl.Logger.Error("failed to create file",
			zap.String("file_id", file.ID.String()),
			zap.Error(err))
		return fmt.Errorf("failed to create file: %w", err)
	}

	// Increment collection file count for active files
	if file.State == dom_file.FileStateActive {
		if err := impl.CollectionRepo.IncrementFileCount(context.Background(), file.CollectionID); err != nil {
			impl.Logger.Error("failed to increment collection file count",
				zap.String("file_id", file.ID.String()),
				zap.String("collection_id", file.CollectionID.String()),
				zap.Error(err))
			// Don't fail the entire operation if count update fails
		}
	}

	impl.Logger.Info("file created successfully",
		zap.String("file_id", file.ID.String()),
		zap.String("collection_id", file.CollectionID.String()))

	return nil
}

func (impl *fileMetadataRepositoryImpl) CreateMany(files []*dom_file.File) error {
	if len(files) == 0 {
		return nil
	}

	batch := impl.Session.NewBatch(gocql.LoggedBatch)

	for _, file := range files {
		if file == nil {
			continue
		}

		// Set timestamps if not set
		if file.CreatedAt.IsZero() {
			file.CreatedAt = time.Now()
		}
		if file.ModifiedAt.IsZero() {
			file.ModifiedAt = file.CreatedAt
		}
		if file.State == "" {
			file.State = dom_file.FileStateActive
		}

		encryptedKeyJSON, err := impl.serializeEncryptedFileKey(file.EncryptedFileKey)
		if err != nil {
			return fmt.Errorf("failed to serialize encrypted file key for file %s: %w", file.ID.String(), err)
		}

		tagsJSON, err := impl.serializeTags(file.Tags)
		if err != nil {
			return fmt.Errorf("failed to serialize tags for file %s: %w", file.ID.String(), err)
		}

		// Add to all 5 tables (same as Create but in batch)
		batch.Query(`INSERT INTO maplefile.files_by_id
			(id, collection_id, owner_id, encrypted_metadata, encrypted_file_key, encryption_version,
			 encrypted_hash, encrypted_file_object_key, encrypted_file_size_in_bytes,
			 encrypted_thumbnail_object_key, encrypted_thumbnail_size_in_bytes, tags,
			 created_at, created_by_user_id, modified_at, modified_by_user_id, version,
			 state, tombstone_version, tombstone_expiry)
			VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
			file.ID, file.CollectionID, file.OwnerID, file.EncryptedMetadata, encryptedKeyJSON,
			file.EncryptionVersion, file.EncryptedHash, file.EncryptedFileObjectKey,
			file.EncryptedFileSizeInBytes, file.EncryptedThumbnailObjectKey,
			file.EncryptedThumbnailSizeInBytes, tagsJSON, file.CreatedAt, file.CreatedByUserID,
			file.ModifiedAt, file.ModifiedByUserID, file.Version, file.State,
			file.TombstoneVersion, file.TombstoneExpiry)

		// 2. Insert into collection table
		batch.Query(`INSERT INTO maplefile.files_by_collection
			(collection_id, modified_at, id, owner_id, encrypted_metadata, encrypted_file_key,
			 encryption_version, encrypted_hash, encrypted_file_object_key, encrypted_file_size_in_bytes,
			 encrypted_thumbnail_object_key, encrypted_thumbnail_size_in_bytes,
			 created_at, created_by_user_id, modified_by_user_id, version,
			 state, tombstone_version, tombstone_expiry)
			VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
			file.CollectionID, file.ModifiedAt, file.ID, file.OwnerID, file.EncryptedMetadata,
			encryptedKeyJSON, file.EncryptionVersion, file.EncryptedHash, file.EncryptedFileObjectKey,
			file.EncryptedFileSizeInBytes, file.EncryptedThumbnailObjectKey,
			file.EncryptedThumbnailSizeInBytes, file.CreatedAt, file.CreatedByUserID,
			file.ModifiedByUserID, file.Version, file.State, file.TombstoneVersion, file.TombstoneExpiry)

		// 3. Insert into owner table
		batch.Query(`INSERT INTO maplefile.files_by_owner
			(owner_id, modified_at, id, collection_id, encrypted_metadata, encrypted_file_key,
			 encryption_version, encrypted_hash, encrypted_file_object_key, encrypted_file_size_in_bytes,
			 encrypted_thumbnail_object_key, encrypted_thumbnail_size_in_bytes,
			 created_at, created_by_user_id, modified_by_user_id, version,
			 state, tombstone_version, tombstone_expiry)
			VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
			file.OwnerID, file.ModifiedAt, file.ID, file.CollectionID, file.EncryptedMetadata,
			encryptedKeyJSON, file.EncryptionVersion, file.EncryptedHash, file.EncryptedFileObjectKey,
			file.EncryptedFileSizeInBytes, file.EncryptedThumbnailObjectKey,
			file.EncryptedThumbnailSizeInBytes, file.CreatedAt, file.CreatedByUserID,
			file.ModifiedByUserID, file.Version, file.State, file.TombstoneVersion, file.TombstoneExpiry)

		// 4. Insert into created_by table
		batch.Query(`INSERT INTO maplefile.files_by_creator
			(created_by_user_id, created_at, id, collection_id, owner_id, encrypted_metadata,
			 encrypted_file_key, encryption_version, encrypted_hash, encrypted_file_object_key,
			 encrypted_file_size_in_bytes, encrypted_thumbnail_object_key, encrypted_thumbnail_size_in_bytes,
			 modified_at, modified_by_user_id, version, state, tombstone_version, tombstone_expiry)
			VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
			file.CreatedByUserID, file.CreatedAt, file.ID, file.CollectionID, file.OwnerID,
			file.EncryptedMetadata, encryptedKeyJSON, file.EncryptionVersion, file.EncryptedHash,
			file.EncryptedFileObjectKey, file.EncryptedFileSizeInBytes, file.EncryptedThumbnailObjectKey,
			file.EncryptedThumbnailSizeInBytes, file.ModifiedAt, file.ModifiedByUserID, file.Version,
			file.State, file.TombstoneVersion, file.TombstoneExpiry)

		// 5. Insert into user sync table (for owner and any collection members)
		batch.Query(`INSERT INTO maplefile.files_by_user
			(user_id, modified_at, id, collection_id, owner_id, encrypted_metadata,
			 encrypted_file_key, encryption_version, encrypted_hash, encrypted_file_object_key,
			 encrypted_file_size_in_bytes, encrypted_thumbnail_object_key, encrypted_thumbnail_size_in_bytes,
			 tags, created_at, created_by_user_id, modified_by_user_id, version,
			 state, tombstone_version, tombstone_expiry)
			VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
			file.OwnerID, file.ModifiedAt, file.ID, file.CollectionID, file.OwnerID,
			file.EncryptedMetadata, encryptedKeyJSON, file.EncryptionVersion, file.EncryptedHash,
			file.EncryptedFileObjectKey, file.EncryptedFileSizeInBytes, file.EncryptedThumbnailObjectKey,
			file.EncryptedThumbnailSizeInBytes, tagsJSON, file.CreatedAt, file.CreatedByUserID,
			file.ModifiedByUserID, file.Version, file.State, file.TombstoneVersion, file.TombstoneExpiry)

		// 6. Insert into denormalized files_by_tag_id table for each tag
		for _, tag := range file.Tags {
			batch.Query(`INSERT INTO maplefile.files_by_tag_id
				(tag_id, file_id, collection_id, owner_id, encrypted_metadata, encrypted_file_key,
				 encryption_version, encrypted_hash, encrypted_file_object_key,
				 encrypted_file_size_in_bytes, encrypted_thumbnail_object_key,
				 encrypted_thumbnail_size_in_bytes, tag_ids, created_at, created_by_user_id,
				 modified_at, modified_by_user_id, version, state, tombstone_version, tombstone_expiry,
				 created_from_ip_address, modified_from_ip_address, ip_anonymized_at)
				VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
				tag.ID, file.ID, file.CollectionID, file.OwnerID, file.EncryptedMetadata,
				encryptedKeyJSON, file.EncryptionVersion, file.EncryptedHash,
				file.EncryptedFileObjectKey, file.EncryptedFileSizeInBytes,
				file.EncryptedThumbnailObjectKey, file.EncryptedThumbnailSizeInBytes,
				tagsJSON, file.CreatedAt, file.CreatedByUserID, file.ModifiedAt,
				file.ModifiedByUserID, file.Version, file.State, file.TombstoneVersion,
				file.TombstoneExpiry,
				nil, nil, nil) // IP tracking fields not yet in domain model
		}
	}

	if err := impl.Session.ExecuteBatch(batch); err != nil {
		impl.Logger.Error("failed to create multiple files", zap.Error(err))
		return fmt.Errorf("failed to create multiple files: %w", err)
	}

	// Increment collection file counts for active files
	// Group by collection to minimize updates
	collectionCounts := make(map[gocql.UUID]int)
	for _, file := range files {
		if file != nil && file.State == dom_file.FileStateActive {
			collectionCounts[file.CollectionID]++
		}
	}

	for collectionID, count := range collectionCounts {
		for i := 0; i < count; i++ {
			if err := impl.CollectionRepo.IncrementFileCount(context.Background(), collectionID); err != nil {
				impl.Logger.Error("failed to increment collection file count",
					zap.String("collection_id", collectionID.String()),
					zap.Error(err))
				// Don't fail the entire operation if count update fails
			}
		}
	}

	impl.Logger.Info("multiple files created successfully", zap.Int("count", len(files)))
	return nil
}
