package collections

import (
	"context"
	"fmt"

	"eq2emu/internal/database"
)

// DatabaseCollectionManager implements CollectionDatabase interface using the existing database wrapper
type DatabaseCollectionManager struct {
	db *database.DB
}

// NewDatabaseCollectionManager creates a new database collection manager
func NewDatabaseCollectionManager(db *database.DB) *DatabaseCollectionManager {
	return &DatabaseCollectionManager{
		db: db,
	}
}

// LoadCollections retrieves all collections from database
func (dcm *DatabaseCollectionManager) LoadCollections(ctx context.Context) ([]CollectionData, error) {
	query := "SELECT `id`, `collection_name`, `collection_category`, `level` FROM `collections`"

	rows, err := dcm.db.QueryContext(ctx, query)
	if err != nil {
		return nil, fmt.Errorf("failed to query collections: %w", err)
	}
	defer rows.Close()

	var collections []CollectionData
	for rows.Next() {
		var collection CollectionData
		err := rows.Scan(
			&collection.ID,
			&collection.Name,
			&collection.Category,
			&collection.Level,
		)
		if err != nil {
			return nil, fmt.Errorf("failed to scan collection row: %w", err)
		}

		collections = append(collections, collection)
	}

	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("error iterating collection rows: %w", err)
	}

	return collections, nil
}

// LoadCollectionItems retrieves items for a specific collection
func (dcm *DatabaseCollectionManager) LoadCollectionItems(ctx context.Context, collectionID int32) ([]CollectionItem, error) {
	query := `SELECT item_id, item_index
			  FROM collection_details
			  WHERE collection_id = ?
			  ORDER BY item_index ASC`

	rows, err := dcm.db.QueryContext(ctx, query, collectionID)
	if err != nil {
		return nil, fmt.Errorf("failed to query collection items for collection %d: %w", collectionID, err)
	}
	defer rows.Close()

	var items []CollectionItem
	for rows.Next() {
		var item CollectionItem
		err := rows.Scan(
			&item.ItemID,
			&item.Index,
		)
		if err != nil {
			return nil, fmt.Errorf("failed to scan collection item row: %w", err)
		}

		// Items start as not found
		item.Found = ItemNotFound
		items = append(items, item)
	}

	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("error iterating collection item rows: %w", err)
	}

	return items, nil
}

// LoadCollectionRewards retrieves rewards for a specific collection
func (dcm *DatabaseCollectionManager) LoadCollectionRewards(ctx context.Context, collectionID int32) ([]CollectionRewardData, error) {
	query := `SELECT collection_id, reward_type, reward_value, reward_quantity
			  FROM collection_rewards
			  WHERE collection_id = ?`

	rows, err := dcm.db.QueryContext(ctx, query, collectionID)
	if err != nil {
		return nil, fmt.Errorf("failed to query collection rewards for collection %d: %w", collectionID, err)
	}
	defer rows.Close()

	var rewards []CollectionRewardData
	for rows.Next() {
		var reward CollectionRewardData
		err := rows.Scan(
			&reward.CollectionID,
			&reward.RewardType,
			&reward.RewardValue,
			&reward.Quantity,
		)
		if err != nil {
			return nil, fmt.Errorf("failed to scan collection reward row: %w", err)
		}

		rewards = append(rewards, reward)
	}

	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("error iterating collection reward rows: %w", err)
	}

	return rewards, nil
}

// LoadPlayerCollections retrieves player's collection progress
func (dcm *DatabaseCollectionManager) LoadPlayerCollections(ctx context.Context, characterID int32) ([]PlayerCollectionData, error) {
	query := `SELECT char_id, collection_id, completed
			  FROM character_collections
			  WHERE char_id = ?`

	rows, err := dcm.db.QueryContext(ctx, query, characterID)
	if err != nil {
		return nil, fmt.Errorf("failed to query player collections for character %d: %w", characterID, err)
	}
	defer rows.Close()

	var collections []PlayerCollectionData
	for rows.Next() {
		var collection PlayerCollectionData
		var completed int
		err := rows.Scan(
			&collection.CharacterID,
			&collection.CollectionID,
			&completed,
		)
		if err != nil {
			return nil, fmt.Errorf("failed to scan player collection row: %w", err)
		}

		collection.Completed = completed == 1
		collections = append(collections, collection)
	}

	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("error iterating player collection rows: %w", err)
	}

	return collections, nil
}

// LoadPlayerCollectionItems retrieves player's found collection items
func (dcm *DatabaseCollectionManager) LoadPlayerCollectionItems(ctx context.Context, characterID, collectionID int32) ([]int32, error) {
	query := `SELECT collection_item_id
			  FROM character_collection_items
			  WHERE char_id = ? AND collection_id = ?`

	rows, err := dcm.db.QueryContext(ctx, query, characterID, collectionID)
	if err != nil {
		return nil, fmt.Errorf("failed to query player collection items for character %d, collection %d: %w", characterID, collectionID, err)
	}
	defer rows.Close()

	var itemIDs []int32
	for rows.Next() {
		var itemID int32
		err := rows.Scan(&itemID)
		if err != nil {
			return nil, fmt.Errorf("failed to scan player collection item row: %w", err)
		}

		itemIDs = append(itemIDs, itemID)
	}

	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("error iterating player collection item rows: %w", err)
	}

	return itemIDs, nil
}

// SavePlayerCollection saves player collection completion status
func (dcm *DatabaseCollectionManager) SavePlayerCollection(ctx context.Context, characterID, collectionID int32, completed bool) error {
	completedInt := 0
	if completed {
		completedInt = 1
	}

	query := `INSERT INTO character_collections (char_id, collection_id, completed)
			  VALUES (?, ?, ?)
			  ON CONFLICT(char_id, collection_id)
			  DO UPDATE SET completed = ?`

	_, err := dcm.db.ExecContext(ctx, query, characterID, collectionID, completedInt, completedInt)
	if err != nil {
		return fmt.Errorf("failed to save player collection for character %d, collection %d: %w", characterID, collectionID, err)
	}

	return nil
}

// SavePlayerCollectionItem saves a found collection item
func (dcm *DatabaseCollectionManager) SavePlayerCollectionItem(ctx context.Context, characterID, collectionID, itemID int32) error {
	query := `INSERT OR IGNORE INTO character_collection_items (char_id, collection_id, collection_item_id)
			  VALUES (?, ?, ?)`

	_, err := dcm.db.ExecContext(ctx, query, characterID, collectionID, itemID)
	if err != nil {
		return fmt.Errorf("failed to save player collection item for character %d, collection %d, item %d: %w", characterID, collectionID, itemID, err)
	}

	return nil
}

// SavePlayerCollections saves all modified player collections
func (dcm *DatabaseCollectionManager) SavePlayerCollections(ctx context.Context, characterID int32, collections []*Collection) error {
	if len(collections) == 0 {
		return nil
	}

	// Use a transaction for atomic updates
	tx, err := dcm.db.BeginTx(ctx, nil)
	if err != nil {
		return fmt.Errorf("failed to begin transaction: %w", err)
	}
	defer tx.Rollback()

	for _, collection := range collections {
		if !collection.GetSaveNeeded() {
			continue
		}

		// Save collection completion status
		if err := dcm.savePlayerCollectionTx(ctx, tx, characterID, collection); err != nil {
			return fmt.Errorf("failed to save collection %d: %w", collection.GetID(), err)
		}

		// Save found items
		if err := dcm.savePlayerCollectionItemsTx(ctx, tx, characterID, collection); err != nil {
			return fmt.Errorf("failed to save collection items for collection %d: %w", collection.GetID(), err)
		}
	}

	if err := tx.Commit(); err != nil {
		return fmt.Errorf("failed to commit transaction: %w", err)
	}

	return nil
}

// savePlayerCollectionTx saves a single collection within a transaction
func (dcm *DatabaseCollectionManager) savePlayerCollectionTx(ctx context.Context, tx database.Tx, characterID int32, collection *Collection) error {
	completedInt := 0
	if collection.GetCompleted() {
		completedInt = 1
	}

	query := `INSERT INTO character_collections (char_id, collection_id, completed)
			  VALUES (?, ?, ?)
			  ON CONFLICT(char_id, collection_id)
			  DO UPDATE SET completed = ?`

	_, err := tx.ExecContext(ctx, query, characterID, collection.GetID(), completedInt, completedInt)
	return err
}

// savePlayerCollectionItemsTx saves collection items within a transaction
func (dcm *DatabaseCollectionManager) savePlayerCollectionItemsTx(ctx context.Context, tx database.Tx, characterID int32, collection *Collection) error {
	items := collection.GetCollectionItems()

	for _, item := range items {
		if item.Found == ItemFound {
			query := `INSERT OR IGNORE INTO character_collection_items (char_id, collection_id, collection_item_id)
					  VALUES (?, ?, ?)`

			_, err := tx.ExecContext(ctx, query, characterID, collection.GetID(), item.ItemID)
			if err != nil {
				return fmt.Errorf("failed to save item %d: %w", item.ItemID, err)
			}
		}
	}

	return nil
}

// EnsureCollectionTables creates the collection tables if they don't exist
func (dcm *DatabaseCollectionManager) EnsureCollectionTables(ctx context.Context) error {
	queries := []string{
		`CREATE TABLE IF NOT EXISTS collections (
			id INTEGER PRIMARY KEY,
			collection_name TEXT NOT NULL,
			collection_category TEXT NOT NULL DEFAULT '',
			level INTEGER NOT NULL DEFAULT 0,
			created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
			updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
		)`,
		`CREATE TABLE IF NOT EXISTS collection_details (
			collection_id INTEGER NOT NULL,
			item_id INTEGER NOT NULL,
			item_index INTEGER NOT NULL DEFAULT 0,
			PRIMARY KEY (collection_id, item_id),
			FOREIGN KEY (collection_id) REFERENCES collections(id) ON DELETE CASCADE
		)`,
		`CREATE TABLE IF NOT EXISTS collection_rewards (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			collection_id INTEGER NOT NULL,
			reward_type TEXT NOT NULL,
			reward_value TEXT NOT NULL,
			reward_quantity INTEGER NOT NULL DEFAULT 1,
			FOREIGN KEY (collection_id) REFERENCES collections(id) ON DELETE CASCADE
		)`,
		`CREATE TABLE IF NOT EXISTS character_collections (
			char_id INTEGER NOT NULL,
			collection_id INTEGER NOT NULL,
			completed INTEGER NOT NULL DEFAULT 0,
			created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
			updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
			PRIMARY KEY (char_id, collection_id),
			FOREIGN KEY (collection_id) REFERENCES collections(id) ON DELETE CASCADE
		)`,
		`CREATE TABLE IF NOT EXISTS character_collection_items (
			char_id INTEGER NOT NULL,
			collection_id INTEGER NOT NULL,
			collection_item_id INTEGER NOT NULL,
			found_at DATETIME DEFAULT CURRENT_TIMESTAMP,
			PRIMARY KEY (char_id, collection_id, collection_item_id),
			FOREIGN KEY (char_id, collection_id) REFERENCES character_collections(char_id, collection_id) ON DELETE CASCADE
		)`,
	}

	for i, query := range queries {
		_, err := dcm.db.ExecContext(ctx, query)
		if err != nil {
			return fmt.Errorf("failed to create collection table %d: %w", i+1, err)
		}
	}

	// Create indexes for better performance
	indexes := []string{
		`CREATE INDEX IF NOT EXISTS idx_collection_details_collection_id ON collection_details(collection_id)`,
		`CREATE INDEX IF NOT EXISTS idx_collection_rewards_collection_id ON collection_rewards(collection_id)`,
		`CREATE INDEX IF NOT EXISTS idx_character_collections_char_id ON character_collections(char_id)`,
		`CREATE INDEX IF NOT EXISTS idx_character_collection_items_char_id ON character_collection_items(char_id)`,
		`CREATE INDEX IF NOT EXISTS idx_character_collection_items_collection_id ON character_collection_items(collection_id)`,
		`CREATE INDEX IF NOT EXISTS idx_collections_category ON collections(collection_category)`,
		`CREATE INDEX IF NOT EXISTS idx_collections_level ON collections(level)`,
	}

	for i, query := range indexes {
		_, err := dcm.db.ExecContext(ctx, query)
		if err != nil {
			return fmt.Errorf("failed to create collection index %d: %w", i+1, err)
		}
	}

	return nil
}

// GetCollectionCount returns the total number of collections in the database
func (dcm *DatabaseCollectionManager) GetCollectionCount(ctx context.Context) (int, error) {
	query := "SELECT COUNT(*) FROM collections"

	var count int
	err := dcm.db.QueryRowContext(ctx, query).Scan(&count)
	if err != nil {
		return 0, fmt.Errorf("failed to get collection count: %w", err)
	}

	return count, nil
}

// GetPlayerCollectionCount returns the number of collections a player has
func (dcm *DatabaseCollectionManager) GetPlayerCollectionCount(ctx context.Context, characterID int32) (int, error) {
	query := "SELECT COUNT(*) FROM character_collections WHERE char_id = ?"

	var count int
	err := dcm.db.QueryRowContext(ctx, query, characterID).Scan(&count)
	if err != nil {
		return 0, fmt.Errorf("failed to get player collection count for character %d: %w", characterID, err)
	}

	return count, nil
}

// GetCompletedCollectionCount returns the number of completed collections for a player
func (dcm *DatabaseCollectionManager) GetCompletedCollectionCount(ctx context.Context, characterID int32) (int, error) {
	query := "SELECT COUNT(*) FROM character_collections WHERE char_id = ? AND completed = 1"

	var count int
	err := dcm.db.QueryRowContext(ctx, query, characterID).Scan(&count)
	if err != nil {
		return 0, fmt.Errorf("failed to get completed collection count for character %d: %w", characterID, err)
	}

	return count, nil
}

// DeletePlayerCollection removes a player's collection progress
func (dcm *DatabaseCollectionManager) DeletePlayerCollection(ctx context.Context, characterID, collectionID int32) error {
	// Use a transaction to ensure both tables are updated atomically
	tx, err := dcm.db.BeginTx(ctx, nil)
	if err != nil {
		return fmt.Errorf("failed to begin transaction: %w", err)
	}
	defer tx.Rollback()

	// Delete collection items first due to foreign key constraint
	_, err = tx.ExecContext(ctx,
		"DELETE FROM character_collection_items WHERE char_id = ? AND collection_id = ?",
		characterID, collectionID)
	if err != nil {
		return fmt.Errorf("failed to delete player collection items: %w", err)
	}

	// Delete collection
	_, err = tx.ExecContext(ctx,
		"DELETE FROM character_collections WHERE char_id = ? AND collection_id = ?",
		characterID, collectionID)
	if err != nil {
		return fmt.Errorf("failed to delete player collection: %w", err)
	}

	if err := tx.Commit(); err != nil {
		return fmt.Errorf("failed to commit transaction: %w", err)
	}

	return nil
}

// GetCollectionStatistics returns database-level collection statistics
func (dcm *DatabaseCollectionManager) GetCollectionStatistics(ctx context.Context) (CollectionStatistics, error) {
	var stats CollectionStatistics

	// Total collections
	err := dcm.db.QueryRowContext(ctx, "SELECT COUNT(*) FROM collections").Scan(&stats.TotalCollections)
	if err != nil {
		return stats, fmt.Errorf("failed to get total collections: %w", err)
	}

	// Total collection items
	err = dcm.db.QueryRowContext(ctx, "SELECT COUNT(*) FROM collection_details").Scan(&stats.TotalItems)
	if err != nil {
		return stats, fmt.Errorf("failed to get total items: %w", err)
	}

	// Players with collections
	err = dcm.db.QueryRowContext(ctx, "SELECT COUNT(DISTINCT char_id) FROM character_collections").Scan(&stats.PlayersWithCollections)
	if err != nil {
		return stats, fmt.Errorf("failed to get players with collections: %w", err)
	}

	// Completed collections across all players
	err = dcm.db.QueryRowContext(ctx, "SELECT COUNT(*) FROM character_collections WHERE completed = 1").Scan(&stats.CompletedCollections)
	if err != nil {
		return stats, fmt.Errorf("failed to get completed collections: %w", err)
	}

	// Active collections (incomplete with at least one item found) across all players
	query := `SELECT COUNT(DISTINCT cc.char_id, cc.collection_id)
			  FROM character_collections cc
			  JOIN character_collection_items cci ON cc.char_id = cci.char_id AND cc.collection_id = cci.collection_id
			  WHERE cc.completed = 0`
	err = dcm.db.QueryRowContext(ctx, query).Scan(&stats.ActiveCollections)
	if err != nil {
		return stats, fmt.Errorf("failed to get active collections: %w", err)
	}

	// Found items across all players
	err = dcm.db.QueryRowContext(ctx, "SELECT COUNT(*) FROM character_collection_items").Scan(&stats.FoundItems)
	if err != nil {
		return stats, fmt.Errorf("failed to get found items: %w", err)
	}

	// Total rewards
	err = dcm.db.QueryRowContext(ctx, "SELECT COUNT(*) FROM collection_rewards").Scan(&stats.TotalRewards)
	if err != nil {
		return stats, fmt.Errorf("failed to get total rewards: %w", err)
	}

	return stats, nil
}
