package achievements

import (
	"context"
	"fmt"
	"time"

	"zombiezen.com/go/sqlite"
	"zombiezen.com/go/sqlite/sqlitex"
)

// LoadAllAchievements loads all achievements from database into master list
func LoadAllAchievements(pool *sqlitex.Pool, masterList *MasterList) error {
	conn, err := pool.Take(context.Background())
	if err != nil {
		return fmt.Errorf("failed to get connection: %w", err)
	}
	defer pool.Put(conn)

	query := `SELECT achievement_id, title, uncompleted_text, completed_text,
			  category, expansion, icon, point_value, qty_req, hide_achievement,
			  unknown3a, unknown3b FROM achievements`

	return sqlitex.Execute(conn, query, &sqlitex.ExecOptions{
		ResultFunc: func(stmt *sqlite.Stmt) error {
			achievement := NewAchievement()
			achievement.ID = uint32(stmt.ColumnInt64(0))
			achievement.Title = stmt.ColumnText(1)
			achievement.UncompletedText = stmt.ColumnText(2)
			achievement.CompletedText = stmt.ColumnText(3)
			achievement.Category = stmt.ColumnText(4)
			achievement.Expansion = stmt.ColumnText(5)
			achievement.Icon = uint16(stmt.ColumnInt64(6))
			achievement.PointValue = uint32(stmt.ColumnInt32(7))
			achievement.QtyRequired = uint32(stmt.ColumnInt64(8))
			achievement.Hide = stmt.ColumnInt64(9) != 0
			achievement.Unknown3A = uint32(stmt.ColumnInt64(10))
			achievement.Unknown3B = uint32(stmt.ColumnInt64(11))

			// Load requirements and rewards
			if err := loadAchievementRequirements(conn, achievement); err != nil {
				return fmt.Errorf("failed to load requirements for achievement %d: %w", achievement.ID, err)
			}

			if err := loadAchievementRewards(conn, achievement); err != nil {
				return fmt.Errorf("failed to load rewards for achievement %d: %w", achievement.ID, err)
			}

			if !masterList.AddAchievement(achievement) {
				return fmt.Errorf("duplicate achievement ID: %d", achievement.ID)
			}

			return nil
		},
	})
}

// loadAchievementRequirements loads requirements for a specific achievement
func loadAchievementRequirements(conn *sqlite.Conn, achievement *Achievement) error {
	query := `SELECT achievement_id, name, qty_req
			  FROM achievements_requirements
			  WHERE achievement_id = ?`

	return sqlitex.Execute(conn, query, &sqlitex.ExecOptions{
		Args: []any{achievement.ID},
		ResultFunc: func(stmt *sqlite.Stmt) error {
			req := Requirement{
				AchievementID: uint32(stmt.ColumnInt64(0)),
				Name:          stmt.ColumnText(1),
				QtyRequired:   uint32(stmt.ColumnInt64(2)),
			}
			achievement.AddRequirement(req)
			return nil
		},
	})
}

// loadAchievementRewards loads rewards for a specific achievement
func loadAchievementRewards(conn *sqlite.Conn, achievement *Achievement) error {
	query := `SELECT achievement_id, reward
			  FROM achievements_rewards
			  WHERE achievement_id = ?`

	return sqlitex.Execute(conn, query, &sqlitex.ExecOptions{
		Args: []any{achievement.ID},
		ResultFunc: func(stmt *sqlite.Stmt) error {
			reward := Reward{
				AchievementID: uint32(stmt.ColumnInt64(0)),
				Reward:        stmt.ColumnText(1),
			}
			achievement.AddReward(reward)
			return nil
		},
	})
}

// LoadPlayerAchievements loads player achievements from database
func LoadPlayerAchievements(pool *sqlitex.Pool, playerID uint32, playerList *PlayerList) error {
	conn, err := pool.Take(context.Background())
	if err != nil {
		return fmt.Errorf("failed to get connection: %w", err)
	}
	defer pool.Put(conn)

	query := `SELECT achievement_id, title, uncompleted_text, completed_text,
			  category, expansion, icon, point_value, qty_req, hide_achievement,
			  unknown3a, unknown3b FROM achievements`

	return sqlitex.Execute(conn, query, &sqlitex.ExecOptions{
		ResultFunc: func(stmt *sqlite.Stmt) error {
			achievement := NewAchievement()
			achievement.ID = uint32(stmt.ColumnInt64(0))
			achievement.Title = stmt.ColumnText(1)
			achievement.UncompletedText = stmt.ColumnText(2)
			achievement.CompletedText = stmt.ColumnText(3)
			achievement.Category = stmt.ColumnText(4)
			achievement.Expansion = stmt.ColumnText(5)
			achievement.Icon = uint16(stmt.ColumnInt64(6))
			achievement.PointValue = uint32(stmt.ColumnInt64(7))
			achievement.QtyRequired = uint32(stmt.ColumnInt64(8))
			achievement.Hide = stmt.ColumnInt64(9) != 0
			achievement.Unknown3A = uint32(stmt.ColumnInt64(10))
			achievement.Unknown3B = uint32(stmt.ColumnInt64(11))

			// Load requirements and rewards
			if err := loadAchievementRequirements(conn, achievement); err != nil {
				return fmt.Errorf("failed to load requirements: %w", err)
			}

			if err := loadAchievementRewards(conn, achievement); err != nil {
				return fmt.Errorf("failed to load rewards: %w", err)
			}

			if !playerList.AddAchievement(achievement) {
				return fmt.Errorf("duplicate achievement ID: %d", achievement.ID)
			}

			return nil
		},
	})
}

// LoadPlayerAchievementUpdates loads player achievement progress from database
func LoadPlayerAchievementUpdates(pool *sqlitex.Pool, playerID uint32, updateList *PlayerUpdateList) error {
	conn, err := pool.Take(context.Background())
	if err != nil {
		return fmt.Errorf("failed to get connection: %w", err)
	}
	defer pool.Put(conn)

	query := `SELECT char_id, achievement_id, completed_date
			  FROM character_achievements
			  WHERE char_id = ?`

	return sqlitex.Execute(conn, query, &sqlitex.ExecOptions{
		Args: []any{playerID},
		ResultFunc: func(stmt *sqlite.Stmt) error {
			update := NewUpdate()
			update.ID = uint32(stmt.ColumnInt64(1))

			// Convert completed_date from Unix timestamp
			if stmt.ColumnType(2) != sqlite.TypeNull {
				timestamp := stmt.ColumnInt64(2)
				update.CompletedDate = time.Unix(timestamp, 0)
			}

			// Load update items
			if err := loadPlayerAchievementUpdateItems(conn, playerID, update); err != nil {
				return fmt.Errorf("failed to load update items: %w", err)
			}

			if !updateList.AddUpdate(update) {
				return fmt.Errorf("duplicate achievement update ID: %d", update.ID)
			}

			return nil
		},
	})
}

// loadPlayerAchievementUpdateItems loads progress items for an achievement update
func loadPlayerAchievementUpdateItems(conn *sqlite.Conn, playerID uint32, update *Update) error {
	query := `SELECT achievement_id, items
			  FROM character_achievements_items
			  WHERE char_id = ? AND achievement_id = ?`

	return sqlitex.Execute(conn, query, &sqlitex.ExecOptions{
		Args: []any{playerID, update.ID},
		ResultFunc: func(stmt *sqlite.Stmt) error {
			item := UpdateItem{
				AchievementID: uint32(stmt.ColumnInt64(0)),
				ItemUpdate:    uint32(stmt.ColumnInt64(1)),
			}
			update.AddUpdateItem(item)
			return nil
		},
	})
}

// SavePlayerAchievementUpdate saves or updates player achievement progress
func SavePlayerAchievementUpdate(pool *sqlitex.Pool, playerID uint32, update *Update) error {
	conn, err := pool.Take(context.Background())
	if err != nil {
		return fmt.Errorf("failed to get connection: %w", err)
	}
	defer pool.Put(conn)

	err = sqlitex.Execute(conn, "BEGIN", nil)
	if err != nil {
		return fmt.Errorf("failed to begin transaction: %w", err)
	}
	defer sqlitex.Execute(conn, "ROLLBACK", nil)

	// Save or update main achievement record
	query := `INSERT OR REPLACE INTO character_achievements
			  (char_id, achievement_id, completed_date) VALUES (?, ?, ?)`

	var completedDate any
	if !update.CompletedDate.IsZero() {
		completedDate = update.CompletedDate.Unix()
	}

	err = sqlitex.Execute(conn, query, &sqlitex.ExecOptions{
		Args: []any{playerID, update.ID, completedDate},
	})
	if err != nil {
		return fmt.Errorf("failed to save achievement update: %w", err)
	}

	// Delete existing update items
	deleteQuery := `DELETE FROM character_achievements_items
				   WHERE char_id = ? AND achievement_id = ?`
	err = sqlitex.Execute(conn, deleteQuery, &sqlitex.ExecOptions{
		Args: []any{playerID, update.ID},
	})
	if err != nil {
		return fmt.Errorf("failed to delete old update items: %w", err)
	}

	// Insert new update items
	itemQuery := `INSERT INTO character_achievements_items
				  (char_id, achievement_id, items) VALUES (?, ?, ?)`
	for _, item := range update.UpdateItems {
		err = sqlitex.Execute(conn, itemQuery, &sqlitex.ExecOptions{
			Args: []any{playerID, item.AchievementID, item.ItemUpdate},
		})
		if err != nil {
			return fmt.Errorf("failed to save update item: %w", err)
		}
	}

	return sqlitex.Execute(conn, "COMMIT", nil)
}

// DeletePlayerAchievementUpdate removes player achievement progress from database
func DeletePlayerAchievementUpdate(pool *sqlitex.Pool, playerID uint32, achievementID uint32) error {
	conn, err := pool.Take(context.Background())
	if err != nil {
		return fmt.Errorf("failed to get connection: %w", err)
	}
	defer pool.Put(conn)

	err = sqlitex.Execute(conn, "BEGIN", nil)
	if err != nil {
		return fmt.Errorf("failed to begin transaction: %w", err)
	}
	defer sqlitex.Execute(conn, "ROLLBACK", nil)

	// Delete main achievement record
	query := `DELETE FROM character_achievements
			  WHERE char_id = ? AND achievement_id = ?`
	err = sqlitex.Execute(conn, query, &sqlitex.ExecOptions{
		Args: []any{playerID, achievementID},
	})
	if err != nil {
		return fmt.Errorf("failed to delete achievement update: %w", err)
	}

	// Delete update items
	itemQuery := `DELETE FROM character_achievements_items
				  WHERE char_id = ? AND achievement_id = ?`
	err = sqlitex.Execute(conn, itemQuery, &sqlitex.ExecOptions{
		Args: []any{playerID, achievementID},
	})
	if err != nil {
		return fmt.Errorf("failed to delete update items: %w", err)
	}

	return sqlitex.Execute(conn, "COMMIT", nil)
}

// SaveAchievement saves or updates an achievement in the database
func SaveAchievement(pool *sqlitex.Pool, achievement *Achievement) error {
	conn, err := pool.Take(context.Background())
	if err != nil {
		return fmt.Errorf("failed to get connection: %w", err)
	}
	defer pool.Put(conn)

	err = sqlitex.Execute(conn, "BEGIN", nil)
	if err != nil {
		return fmt.Errorf("failed to begin transaction: %w", err)
	}
	defer sqlitex.Execute(conn, "ROLLBACK", nil)

	// Save main achievement record
	query := `INSERT OR REPLACE INTO achievements
			  (achievement_id, title, uncompleted_text, completed_text,
			   category, expansion, icon, point_value, qty_req,
			   hide_achievement, unknown3a, unknown3b)
			  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`

	err = sqlitex.Execute(conn, query, &sqlitex.ExecOptions{
		Args: []any{
			achievement.ID, achievement.Title,
			achievement.UncompletedText, achievement.CompletedText,
			achievement.Category, achievement.Expansion, achievement.Icon,
			achievement.PointValue, achievement.QtyRequired, achievement.Hide,
			achievement.Unknown3A, achievement.Unknown3B,
		},
	})
	if err != nil {
		return fmt.Errorf("failed to save achievement: %w", err)
	}

	// Delete existing requirements and rewards
	err = sqlitex.Execute(conn, "DELETE FROM achievements_requirements WHERE achievement_id = ?", &sqlitex.ExecOptions{
		Args: []any{achievement.ID},
	})
	if err != nil {
		return fmt.Errorf("failed to delete old requirements: %w", err)
	}

	err = sqlitex.Execute(conn, "DELETE FROM achievements_rewards WHERE achievement_id = ?", &sqlitex.ExecOptions{
		Args: []any{achievement.ID},
	})
	if err != nil {
		return fmt.Errorf("failed to delete old rewards: %w", err)
	}

	// Insert requirements
	reqQuery := `INSERT INTO achievements_requirements
				 (achievement_id, name, qty_req) VALUES (?, ?, ?)`
	for _, req := range achievement.Requirements {
		err = sqlitex.Execute(conn, reqQuery, &sqlitex.ExecOptions{
			Args: []any{req.AchievementID, req.Name, req.QtyRequired},
		})
		if err != nil {
			return fmt.Errorf("failed to save requirement: %w", err)
		}
	}

	// Insert rewards
	rewardQuery := `INSERT INTO achievements_rewards
				   (achievement_id, reward) VALUES (?, ?)`
	for _, reward := range achievement.Rewards {
		err = sqlitex.Execute(conn, rewardQuery, &sqlitex.ExecOptions{
			Args: []any{reward.AchievementID, reward.Reward},
		})
		if err != nil {
			return fmt.Errorf("failed to save reward: %w", err)
		}
	}

	return sqlitex.Execute(conn, "COMMIT", nil)
}

// DeleteAchievement removes an achievement and all related records from database
func DeleteAchievement(pool *sqlitex.Pool, achievementID uint32) error {
	conn, err := pool.Take(context.Background())
	if err != nil {
		return fmt.Errorf("failed to get connection: %w", err)
	}
	defer pool.Put(conn)

	err = sqlitex.Execute(conn, "BEGIN", nil)
	if err != nil {
		return fmt.Errorf("failed to begin transaction: %w", err)
	}
	defer sqlitex.Execute(conn, "ROLLBACK", nil)

	// Delete main achievement
	err = sqlitex.Execute(conn, "DELETE FROM achievements WHERE achievement_id = ?", &sqlitex.ExecOptions{
		Args: []any{achievementID},
	})
	if err != nil {
		return fmt.Errorf("failed to delete achievement: %w", err)
	}

	// Delete requirements
	err = sqlitex.Execute(conn, "DELETE FROM achievements_requirements WHERE achievement_id = ?", &sqlitex.ExecOptions{
		Args: []any{achievementID},
	})
	if err != nil {
		return fmt.Errorf("failed to delete requirements: %w", err)
	}

	// Delete rewards
	err = sqlitex.Execute(conn, "DELETE FROM achievements_rewards WHERE achievement_id = ?", &sqlitex.ExecOptions{
		Args: []any{achievementID},
	})
	if err != nil {
		return fmt.Errorf("failed to delete rewards: %w", err)
	}

	// Delete player progress (optional - might want to preserve history)
	err = sqlitex.Execute(conn, "DELETE FROM character_achievements WHERE achievement_id = ?", &sqlitex.ExecOptions{
		Args: []any{achievementID},
	})
	if err != nil {
		return fmt.Errorf("failed to delete player achievements: %w", err)
	}

	err = sqlitex.Execute(conn, "DELETE FROM character_achievements_items WHERE achievement_id = ?", &sqlitex.ExecOptions{
		Args: []any{achievementID},
	})
	if err != nil {
		return fmt.Errorf("failed to delete player achievement items: %w", err)
	}

	return sqlitex.Execute(conn, "COMMIT", nil)
}
