package tradeskills

import (
	"database/sql"
	"fmt"
	"log"
)

// DatabaseService handles database operations for the tradeskills system.
type DatabaseService interface {
	// LoadTradeskillEvents loads all tradeskill events from the database
	LoadTradeskillEvents(masterList *MasterTradeskillEventsList) error

	// CreateTradeskillTables creates the required database tables
	CreateTradeskillTables() error

	// SaveTradeskillEvent saves a tradeskill event to the database
	SaveTradeskillEvent(event *TradeskillEvent) error

	// DeleteTradeskillEvent removes a tradeskill event from the database
	DeleteTradeskillEvent(name string, technique uint32) error
}

// SQLiteTradeskillDatabase implements DatabaseService for SQLite.
type SQLiteTradeskillDatabase struct {
	db *sql.DB
}

// NewSQLiteTradeskillDatabase creates a new SQLite database service.
func NewSQLiteTradeskillDatabase(db *sql.DB) *SQLiteTradeskillDatabase {
	return &SQLiteTradeskillDatabase{
		db: db,
	}
}

// CreateTradeskillTables creates the required database tables for tradeskills.
func (db *SQLiteTradeskillDatabase) CreateTradeskillTables() error {
	createTableSQL := `
	CREATE TABLE IF NOT EXISTS tradeskillevents (
		id INTEGER PRIMARY KEY AUTOINCREMENT,
		name TEXT NOT NULL,
		icon INTEGER NOT NULL,
		technique INTEGER NOT NULL,
		success_progress INTEGER NOT NULL DEFAULT 0,
		success_durability INTEGER NOT NULL DEFAULT 0,
		success_hp INTEGER NOT NULL DEFAULT 0,
		success_power INTEGER NOT NULL DEFAULT 0,
		success_spell_id INTEGER NOT NULL DEFAULT 0,
		success_item_id INTEGER NOT NULL DEFAULT 0,
		fail_progress INTEGER NOT NULL DEFAULT 0,
		fail_durability INTEGER NOT NULL DEFAULT 0,
		fail_hp INTEGER NOT NULL DEFAULT 0,
		fail_power INTEGER NOT NULL DEFAULT 0,
		created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
		updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
		UNIQUE(name, technique)
	);
	
	CREATE INDEX IF NOT EXISTS idx_tradeskillevents_technique ON tradeskillevents(technique);
	CREATE INDEX IF NOT EXISTS idx_tradeskillevents_name ON tradeskillevents(name);
	`

	_, err := db.db.Exec(createTableSQL)
	if err != nil {
		return fmt.Errorf("failed to create tradeskillevents table: %w", err)
	}

	log.Printf("Created tradeskillevents table")
	return nil
}

// LoadTradeskillEvents loads all tradeskill events from the database into the master list.
func (db *SQLiteTradeskillDatabase) LoadTradeskillEvents(masterList *MasterTradeskillEventsList) error {
	if masterList == nil {
		return fmt.Errorf("masterList cannot be nil")
	}

	query := `
	SELECT name, icon, technique, success_progress, success_durability, 
	       success_hp, success_power, success_spell_id, success_item_id,
	       fail_progress, fail_durability, fail_hp, fail_power
	FROM tradeskillevents
	ORDER BY technique, name
	`

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

	eventsLoaded := 0

	for rows.Next() {
		event := &TradeskillEvent{}

		err := rows.Scan(
			&event.Name,
			&event.Icon,
			&event.Technique,
			&event.SuccessProgress,
			&event.SuccessDurability,
			&event.SuccessHP,
			&event.SuccessPower,
			&event.SuccessSpellID,
			&event.SuccessItemID,
			&event.FailProgress,
			&event.FailDurability,
			&event.FailHP,
			&event.FailPower,
		)

		if err != nil {
			log.Printf("Warning: Failed to scan tradeskill event row: %v", err)
			continue
		}

		// Validate the event
		if event.Name == "" {
			log.Printf("Warning: Skipping tradeskill event with empty name")
			continue
		}

		if !IsValidTechnique(event.Technique) {
			log.Printf("Warning: Skipping tradeskill event '%s' with invalid technique %d",
				event.Name, event.Technique)
			continue
		}

		masterList.AddEvent(event)
		eventsLoaded++

		log.Printf("Loaded tradeskill event: %s (technique: %d)", event.Name, event.Technique)
	}

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

	log.Printf("Loaded %d tradeskill events", eventsLoaded)
	return nil
}

// SaveTradeskillEvent saves a tradeskill event to the database.
func (db *SQLiteTradeskillDatabase) SaveTradeskillEvent(event *TradeskillEvent) error {
	if event == nil {
		return fmt.Errorf("event cannot be nil")
	}

	if event.Name == "" {
		return fmt.Errorf("event name cannot be empty")
	}

	if !IsValidTechnique(event.Technique) {
		return fmt.Errorf("invalid technique: %d", event.Technique)
	}

	insertSQL := `
	INSERT OR REPLACE INTO tradeskillevents (
		name, icon, technique, success_progress, success_durability,
		success_hp, success_power, success_spell_id, success_item_id,
		fail_progress, fail_durability, fail_hp, fail_power, updated_at
	) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
	`

	_, err := db.db.Exec(insertSQL,
		event.Name,
		event.Icon,
		event.Technique,
		event.SuccessProgress,
		event.SuccessDurability,
		event.SuccessHP,
		event.SuccessPower,
		event.SuccessSpellID,
		event.SuccessItemID,
		event.FailProgress,
		event.FailDurability,
		event.FailHP,
		event.FailPower,
	)

	if err != nil {
		return fmt.Errorf("failed to save tradeskill event '%s': %w", event.Name, err)
	}

	log.Printf("Saved tradeskill event: %s", event.Name)
	return nil
}

// DeleteTradeskillEvent removes a tradeskill event from the database.
func (db *SQLiteTradeskillDatabase) DeleteTradeskillEvent(name string, technique uint32) error {
	if name == "" {
		return fmt.Errorf("name cannot be empty")
	}

	deleteSQL := `DELETE FROM tradeskillevents WHERE name = ? AND technique = ?`

	result, err := db.db.Exec(deleteSQL, name, technique)
	if err != nil {
		return fmt.Errorf("failed to delete tradeskill event '%s': %w", name, err)
	}

	rowsAffected, err := result.RowsAffected()
	if err != nil {
		return fmt.Errorf("failed to get rows affected for event '%s': %w", name, err)
	}

	if rowsAffected == 0 {
		return fmt.Errorf("tradeskill event '%s' with technique %d not found", name, technique)
	}

	log.Printf("Deleted tradeskill event: %s (technique: %d)", name, technique)
	return nil
}

// GetTradeskillEventsByTechnique retrieves all events for a specific technique from the database.
func (db *SQLiteTradeskillDatabase) GetTradeskillEventsByTechnique(technique uint32) ([]*TradeskillEvent, error) {
	if !IsValidTechnique(technique) {
		return nil, fmt.Errorf("invalid technique: %d", technique)
	}

	query := `
	SELECT name, icon, technique, success_progress, success_durability,
	       success_hp, success_power, success_spell_id, success_item_id,
	       fail_progress, fail_durability, fail_hp, fail_power
	FROM tradeskillevents
	WHERE technique = ?
	ORDER BY name
	`

	rows, err := db.db.Query(query, technique)
	if err != nil {
		return nil, fmt.Errorf("failed to query events for technique %d: %w", technique, err)
	}
	defer rows.Close()

	var events []*TradeskillEvent

	for rows.Next() {
		event := &TradeskillEvent{}

		err := rows.Scan(
			&event.Name,
			&event.Icon,
			&event.Technique,
			&event.SuccessProgress,
			&event.SuccessDurability,
			&event.SuccessHP,
			&event.SuccessPower,
			&event.SuccessSpellID,
			&event.SuccessItemID,
			&event.FailProgress,
			&event.FailDurability,
			&event.FailHP,
			&event.FailPower,
		)

		if err != nil {
			log.Printf("Warning: Failed to scan event row: %v", err)
			continue
		}

		events = append(events, event)
	}

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

	return events, nil
}

// GetTradeskillEventByName retrieves a specific event by name and technique.
func (db *SQLiteTradeskillDatabase) GetTradeskillEventByName(name string, technique uint32) (*TradeskillEvent, error) {
	if name == "" {
		return nil, fmt.Errorf("name cannot be empty")
	}

	if !IsValidTechnique(technique) {
		return nil, fmt.Errorf("invalid technique: %d", technique)
	}

	query := `
	SELECT name, icon, technique, success_progress, success_durability,
	       success_hp, success_power, success_spell_id, success_item_id,
	       fail_progress, fail_durability, fail_hp, fail_power
	FROM tradeskillevents
	WHERE name = ? AND technique = ?
	`

	row := db.db.QueryRow(query, name, technique)

	event := &TradeskillEvent{}
	err := row.Scan(
		&event.Name,
		&event.Icon,
		&event.Technique,
		&event.SuccessProgress,
		&event.SuccessDurability,
		&event.SuccessHP,
		&event.SuccessPower,
		&event.SuccessSpellID,
		&event.SuccessItemID,
		&event.FailProgress,
		&event.FailDurability,
		&event.FailHP,
		&event.FailPower,
	)

	if err != nil {
		if err == sql.ErrNoRows {
			return nil, fmt.Errorf("tradeskill event '%s' with technique %d not found", name, technique)
		}
		return nil, fmt.Errorf("failed to get tradeskill event '%s': %w", name, err)
	}

	return event, nil
}

// CountTradeskillEvents returns the total number of events in the database.
func (db *SQLiteTradeskillDatabase) CountTradeskillEvents() (int32, error) {
	query := `SELECT COUNT(*) FROM tradeskillevents`

	var count int32
	err := db.db.QueryRow(query).Scan(&count)
	if err != nil {
		return 0, fmt.Errorf("failed to count tradeskill events: %w", err)
	}

	return count, nil
}

// GetTechniqueCounts returns the number of events per technique.
func (db *SQLiteTradeskillDatabase) GetTechniqueCounts() (map[uint32]int32, error) {
	query := `
	SELECT technique, COUNT(*) as count
	FROM tradeskillevents
	GROUP BY technique
	ORDER BY technique
	`

	rows, err := db.db.Query(query)
	if err != nil {
		return nil, fmt.Errorf("failed to query technique counts: %w", err)
	}
	defer rows.Close()

	counts := make(map[uint32]int32)

	for rows.Next() {
		var technique uint32
		var count int32

		err := rows.Scan(&technique, &count)
		if err != nil {
			log.Printf("Warning: Failed to scan technique count row: %v", err)
			continue
		}

		counts[technique] = count
	}

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

	return counts, nil
}

// InsertDefaultTradeskillEvents adds some default tradeskill events to the database.
func (db *SQLiteTradeskillDatabase) InsertDefaultTradeskillEvents() error {
	defaultEvents := []*TradeskillEvent{
		{
			Name:              "Blazing Heat",
			Icon:              1234,
			Technique:         TechniqueSkillAlchemy,
			SuccessProgress:   25,
			SuccessDurability: 0,
			SuccessHP:         0,
			SuccessPower:      -10,
			SuccessSpellID:    0,
			SuccessItemID:     0,
			FailProgress:      -10,
			FailDurability:    -25,
			FailHP:            0,
			FailPower:         0,
		},
		{
			Name:              "Precise Cut",
			Icon:              5678,
			Technique:         TechniqueSkillFletching,
			SuccessProgress:   30,
			SuccessDurability: 5,
			SuccessHP:         0,
			SuccessPower:      -5,
			SuccessSpellID:    0,
			SuccessItemID:     0,
			FailProgress:      0,
			FailDurability:    -30,
			FailHP:            0,
			FailPower:         0,
		},
		{
			Name:              "Perfect Stitch",
			Icon:              9012,
			Technique:         TechniqueSkillTailoring,
			SuccessProgress:   20,
			SuccessDurability: 10,
			SuccessHP:         0,
			SuccessPower:      -8,
			SuccessSpellID:    0,
			SuccessItemID:     0,
			FailProgress:      -5,
			FailDurability:    -15,
			FailHP:            0,
			FailPower:         0,
		},
	}

	for _, event := range defaultEvents {
		err := db.SaveTradeskillEvent(event)
		if err != nil {
			log.Printf("Warning: Failed to insert default event '%s': %v", event.Name, err)
		}
	}

	log.Printf("Inserted %d default tradeskill events", len(defaultEvents))
	return nil
}
