// monorepo/cloud/maplefile-backend/internal/maplefile/repo/collection/count.go
package collection

import (
	"context"
	"fmt"
	"time"

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

	dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
)

// CountOwnedCollections counts all collections (folders + albums) owned by the user
func (impl *collectionRepositoryImpl) CountOwnedCollections(ctx context.Context, userID gocql.UUID) (int, error) {
	return impl.countCollectionsByUserAndType(ctx, userID, "owner", "")
}

// CountSharedCollections counts all collections (folders + albums) shared with the user
func (impl *collectionRepositoryImpl) CountSharedCollections(ctx context.Context, userID gocql.UUID) (int, error) {
	return impl.countCollectionsByUserAndType(ctx, userID, "member", "")
}

// CountOwnedFolders counts only folders owned by the user
func (impl *collectionRepositoryImpl) CountOwnedFolders(ctx context.Context, userID gocql.UUID) (int, error) {
	return impl.countCollectionsByUserAndType(ctx, userID, "owner", dom_collection.CollectionTypeFolder)
}

// CountSharedFolders counts only folders shared with the user
func (impl *collectionRepositoryImpl) CountSharedFolders(ctx context.Context, userID gocql.UUID) (int, error) {
	return impl.countCollectionsByUserAndType(ctx, userID, "member", dom_collection.CollectionTypeFolder)
}

// countCollectionsByUserAndType is a helper method that efficiently counts collections
// filterType: empty string for all types, or specific type like "folder"
func (impl *collectionRepositoryImpl) countCollectionsByUserAndType(ctx context.Context, userID gocql.UUID, accessType, filterType string) (int, error) {
	// Use the access-type-specific table for efficient querying
	query := `SELECT collection_id FROM maplefile.collections_by_user_id_and_access_type_with_desc_modified_at_and_asc_collection_id
		WHERE user_id = ? AND access_type = ?`

	impl.Logger.Debug("Starting collection count query",
		zap.String("user_id", userID.String()),
		zap.String("access_type", accessType),
		zap.String("filter_type", filterType))

	iter := impl.Session.Query(query, userID, accessType).WithContext(ctx).Iter()

	count := 0
	totalScanned := 0
	var collectionID gocql.UUID
	var debugCollectionIDs []string

	// Iterate through results and count based on criteria
	for iter.Scan(&collectionID) {
		totalScanned++
		debugCollectionIDs = append(debugCollectionIDs, collectionID.String())

		impl.Logger.Debug("Processing collection for count",
			zap.String("collection_id", collectionID.String()),
			zap.Int("total_scanned", totalScanned),
			zap.String("access_type", accessType))

		// Get the collection to check state and type
		collection, err := impl.getBaseCollection(ctx, collectionID)
		if err != nil {
			impl.Logger.Warn("failed to get collection for counting",
				zap.String("collection_id", collectionID.String()),
				zap.Error(err))
			continue
		}

		if collection == nil {
			impl.Logger.Warn("collection not found for counting",
				zap.String("collection_id", collectionID.String()))
			continue
		}

		impl.Logger.Debug("Collection details for counting",
			zap.String("collection_id", collectionID.String()),
			zap.String("state", collection.State),
			zap.String("collection_type", collection.CollectionType),
			zap.String("owner_id", collection.OwnerID.String()),
			zap.String("querying_user_id", userID.String()),
			zap.String("access_type", accessType),
			zap.String("required_filter_type", filterType))

		// Only count active collections
		if collection.State != dom_collection.CollectionStateActive {
			impl.Logger.Debug("Skipping collection due to non-active state",
				zap.String("collection_id", collectionID.String()),
				zap.String("state", collection.State))
			continue
		}

		// Filter by type if specified
		if filterType != "" && collection.CollectionType != filterType {
			impl.Logger.Debug("Skipping collection due to type filter",
				zap.String("collection_id", collectionID.String()),
				zap.String("collection_type", collection.CollectionType),
				zap.String("required_type", filterType))
			continue
		}

		count++
		impl.Logger.Info("Collection counted",
			zap.String("collection_id", collectionID.String()),
			zap.String("access_type", accessType),
			zap.String("owner_id", collection.OwnerID.String()),
			zap.String("querying_user_id", userID.String()),
			zap.Bool("is_owner", collection.OwnerID == userID),
			zap.Int("current_count", count))
	}

	if err := iter.Close(); err != nil {
		impl.Logger.Error("failed to count collections",
			zap.String("user_id", userID.String()),
			zap.String("access_type", accessType),
			zap.String("filter_type", filterType),
			zap.Error(err))
		return 0, fmt.Errorf("failed to count collections: %w", err)
	}

	impl.Logger.Info("Collection count completed",
		zap.String("user_id", userID.String()),
		zap.String("access_type", accessType),
		zap.String("filter_type", filterType),
		zap.Int("final_count", count),
		zap.Int("total_scanned", totalScanned),
		zap.Strings("scanned_collection_ids", debugCollectionIDs))

	return count, nil
}

// FIXED DEBUG: Query both access types separately to avoid ALLOW FILTERING
func (impl *collectionRepositoryImpl) DebugCollectionRecords(ctx context.Context, userID gocql.UUID) error {
	impl.Logger.Info("=== DEBUG: Checking OWNER records ===")

	// Check owner records
	ownerQuery := `SELECT user_id, access_type, modified_at, collection_id, permission_level, state
		FROM maplefile.collections_by_user_id_and_access_type_with_desc_modified_at_and_asc_collection_id
		WHERE user_id = ? AND access_type = ?`

	ownerIter := impl.Session.Query(ownerQuery, userID, "owner").WithContext(ctx).Iter()

	var (
		resultUserID    gocql.UUID
		accessType      string
		modifiedAt      time.Time
		collectionID    gocql.UUID
		permissionLevel string
		state           string
	)

	ownerCount := 0
	for ownerIter.Scan(&resultUserID, &accessType, &modifiedAt, &collectionID, &permissionLevel, &state) {
		ownerCount++
		impl.Logger.Info("DEBUG: Found OWNER record",
			zap.Int("record_number", ownerCount),
			zap.String("user_id", resultUserID.String()),
			zap.String("access_type", accessType),
			zap.Time("modified_at", modifiedAt),
			zap.String("collection_id", collectionID.String()),
			zap.String("permission_level", permissionLevel),
			zap.String("state", state))
	}
	ownerIter.Close()

	impl.Logger.Info("=== DEBUG: Checking MEMBER records ===")

	// Check member records
	memberIter := impl.Session.Query(ownerQuery, userID, "member").WithContext(ctx).Iter()

	memberCount := 0
	for memberIter.Scan(&resultUserID, &accessType, &modifiedAt, &collectionID, &permissionLevel, &state) {
		memberCount++
		impl.Logger.Info("DEBUG: Found MEMBER record",
			zap.Int("record_number", memberCount),
			zap.String("user_id", resultUserID.String()),
			zap.String("access_type", accessType),
			zap.Time("modified_at", modifiedAt),
			zap.String("collection_id", collectionID.String()),
			zap.String("permission_level", permissionLevel),
			zap.String("state", state))
	}
	memberIter.Close()

	impl.Logger.Info("DEBUG: Total records summary",
		zap.String("user_id", userID.String()),
		zap.Int("owner_records", ownerCount),
		zap.Int("member_records", memberCount),
		zap.Int("total_records", ownerCount+memberCount))

	return nil
}

// Alternative optimized implementation for when you need both owned and shared counts
// This reduces database round trips by querying once and separating in memory
func (impl *collectionRepositoryImpl) countCollectionsSummary(ctx context.Context, userID gocql.UUID, filterType string) (ownedCount, sharedCount int, err error) {
	// Query all collections for the user using the general table
	query := `SELECT collection_id, access_type FROM maplefile.collections_by_user_id_with_desc_modified_at_and_asc_collection_id
		WHERE user_id = ?`

	iter := impl.Session.Query(query, userID).WithContext(ctx).Iter()

	var collectionID gocql.UUID
	var accessType string

	for iter.Scan(&collectionID, &accessType) {
		// Get the collection to check state and type
		collection, getErr := impl.getBaseCollection(ctx, collectionID)
		if getErr != nil {
			impl.Logger.Warn("failed to get collection for counting summary",
				zap.String("collection_id", collectionID.String()),
				zap.Error(getErr))
			continue
		}

		if collection == nil {
			continue
		}

		// Only count active collections
		if collection.State != dom_collection.CollectionStateActive {
			continue
		}

		// Filter by type if specified
		if filterType != "" && collection.CollectionType != filterType {
			continue
		}

		// Count based on access type
		switch accessType {
		case "owner":
			ownedCount++
		case "member":
			sharedCount++
		}
	}

	if err = iter.Close(); err != nil {
		impl.Logger.Error("failed to count collections summary",
			zap.String("user_id", userID.String()),
			zap.String("filter_type", filterType),
			zap.Error(err))
		return 0, 0, fmt.Errorf("failed to count collections summary: %w", err)
	}

	impl.Logger.Debug("counted collections summary successfully",
		zap.String("user_id", userID.String()),
		zap.String("filter_type", filterType),
		zap.Int("owned_count", ownedCount),
		zap.Int("shared_count", sharedCount))

	return ownedCount, sharedCount, nil
}

// CountTotalUniqueFolders counts unique folders accessible to the user (deduplicates owned+shared)
func (impl *collectionRepositoryImpl) CountTotalUniqueFolders(ctx context.Context, userID gocql.UUID) (int, error) {
	// Use a set to track unique collection IDs to avoid double-counting
	uniqueCollectionIDs := make(map[gocql.UUID]bool)

	// Query all collections for the user using the general table
	query := `SELECT collection_id FROM maplefile.collections_by_user_id_with_desc_modified_at_and_asc_collection_id
		WHERE user_id = ?`

	iter := impl.Session.Query(query, userID).WithContext(ctx).Iter()

	var collectionID gocql.UUID
	totalScanned := 0

	for iter.Scan(&collectionID) {
		totalScanned++

		// Get the collection to check state and type
		collection, err := impl.getBaseCollection(ctx, collectionID)
		if err != nil {
			impl.Logger.Warn("failed to get collection for unique counting",
				zap.String("collection_id", collectionID.String()),
				zap.Error(err))
			continue
		}

		if collection == nil {
			continue
		}

		impl.Logger.Debug("Processing collection for unique count",
			zap.String("collection_id", collectionID.String()),
			zap.String("state", collection.State),
			zap.String("collection_type", collection.CollectionType),
			zap.Int("total_scanned", totalScanned))

		// Only count active folders
		if collection.State != dom_collection.CollectionStateActive {
			impl.Logger.Debug("Skipping collection due to non-active state",
				zap.String("collection_id", collectionID.String()),
				zap.String("state", collection.State))
			continue
		}

		// Filter by folder type
		if collection.CollectionType != dom_collection.CollectionTypeFolder {
			impl.Logger.Debug("Skipping collection due to type filter",
				zap.String("collection_id", collectionID.String()),
				zap.String("collection_type", collection.CollectionType))
			continue
		}

		// Add to unique set (automatically deduplicates)
		uniqueCollectionIDs[collectionID] = true

		impl.Logger.Debug("Added unique folder to count",
			zap.String("collection_id", collectionID.String()),
			zap.Int("current_unique_count", len(uniqueCollectionIDs)))
	}

	if err := iter.Close(); err != nil {
		impl.Logger.Error("failed to count unique folders",
			zap.String("user_id", userID.String()),
			zap.Error(err))
		return 0, fmt.Errorf("failed to count unique folders: %w", err)
	}

	uniqueCount := len(uniqueCollectionIDs)

	impl.Logger.Info("Unique folder count completed",
		zap.String("user_id", userID.String()),
		zap.Int("total_scanned", totalScanned),
		zap.Int("unique_folders", uniqueCount))

	return uniqueCount, nil
}
