package factions

import (
	"context"
	"fmt"
	"time"

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

// DatabaseAdapter implements the factions.Database interface using sqlitex.Pool
type DatabaseAdapter struct {
	pool *sqlitex.Pool
}

// NewDatabaseAdapter creates a new database adapter for factions
func NewDatabaseAdapter(pool *sqlitex.Pool) *DatabaseAdapter {
	return &DatabaseAdapter{pool: pool}
}

// LoadAllFactions loads all factions from the database
func (da *DatabaseAdapter) LoadAllFactions() ([]*Faction, error) {
	conn, err := da.pool.Take(context.Background())
	if err != nil {
		return nil, fmt.Errorf("failed to get connection: %w", err)
	}
	defer da.pool.Put(conn)

	// Create factions table if it doesn't exist
	err = sqlitex.Execute(conn, `
		CREATE TABLE IF NOT EXISTS factions (
			id INTEGER PRIMARY KEY,
			name TEXT NOT NULL,
			type TEXT,
			description TEXT,
			negative_change INTEGER DEFAULT 0,
			positive_change INTEGER DEFAULT 0,
			default_value INTEGER DEFAULT 0,
			created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
			updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
		)
	`, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create factions table: %w", err)
	}

	var factions []*Faction
	err = sqlitex.Execute(conn, "SELECT id, name, type, description, negative_change, positive_change, default_value FROM factions", &sqlitex.ExecOptions{
		ResultFunc: func(stmt *sqlite.Stmt) error {
			faction := &Faction{
				ID:             int32(stmt.ColumnInt64(0)),
				Name:           stmt.ColumnText(1),
				Type:           stmt.ColumnText(2),
				Description:    stmt.ColumnText(3),
				NegativeChange: int16(stmt.ColumnInt64(4)),
				PositiveChange: int16(stmt.ColumnInt64(5)),
				DefaultValue:   int32(stmt.ColumnInt64(6)),
			}
			factions = append(factions, faction)
			return nil
		},
	})

	if err != nil {
		return nil, fmt.Errorf("failed to load factions: %w", err)
	}

	return factions, nil
}

// SaveFaction saves a faction to the database
func (da *DatabaseAdapter) SaveFaction(faction *Faction) error {
	if faction == nil {
		return fmt.Errorf("faction is nil")
	}

	conn, err := da.pool.Take(context.Background())
	if err != nil {
		return fmt.Errorf("failed to get connection: %w", err)
	}
	defer da.pool.Put(conn)

	// Use INSERT OR REPLACE to handle both insert and update
	err = sqlitex.Execute(conn, `
		INSERT OR REPLACE INTO factions (id, name, type, description, negative_change, positive_change, default_value, updated_at)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?)
	`, &sqlitex.ExecOptions{
		Args: []any{faction.ID, faction.Name, faction.Type, faction.Description, 
			faction.NegativeChange, faction.PositiveChange, faction.DefaultValue, time.Now().Unix()},
	})

	if err != nil {
		return fmt.Errorf("failed to save faction %d: %w", faction.ID, err)
	}

	return nil
}

// DeleteFaction deletes a faction from the database
func (da *DatabaseAdapter) DeleteFaction(factionID int32) error {
	conn, err := da.pool.Take(context.Background())
	if err != nil {
		return fmt.Errorf("failed to get connection: %w", err)
	}
	defer da.pool.Put(conn)

	err = sqlitex.Execute(conn, "DELETE FROM factions WHERE id = ?", &sqlitex.ExecOptions{
		Args: []any{factionID},
	})
	if err != nil {
		return fmt.Errorf("failed to delete faction %d: %w", factionID, err)
	}

	return nil
}

// LoadHostileFactionRelations loads all hostile faction relations
func (da *DatabaseAdapter) LoadHostileFactionRelations() ([]*FactionRelation, error) {
	conn, err := da.pool.Take(context.Background())
	if err != nil {
		return nil, fmt.Errorf("failed to get connection: %w", err)
	}
	defer da.pool.Put(conn)

	// Create faction_relations table if it doesn't exist
	err = sqlitex.Execute(conn, `
		CREATE TABLE IF NOT EXISTS faction_relations (
			faction_id INTEGER NOT NULL,
			related_faction_id INTEGER NOT NULL,
			is_hostile INTEGER NOT NULL DEFAULT 0,
			created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
			PRIMARY KEY (faction_id, related_faction_id),
			FOREIGN KEY (faction_id) REFERENCES factions(id),
			FOREIGN KEY (related_faction_id) REFERENCES factions(id)
		)
	`, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create faction_relations table: %w", err)
	}

	var relations []*FactionRelation
	err = sqlitex.Execute(conn, "SELECT faction_id, related_faction_id FROM faction_relations WHERE is_hostile = 1", &sqlitex.ExecOptions{
		ResultFunc: func(stmt *sqlite.Stmt) error {
			relation := &FactionRelation{
				FactionID:        int32(stmt.ColumnInt64(0)),
				HostileFactionID: int32(stmt.ColumnInt64(1)),
			}
			relations = append(relations, relation)
			return nil
		},
	})

	if err != nil {
		return nil, fmt.Errorf("failed to load hostile faction relations: %w", err)
	}

	return relations, nil
}

// LoadFriendlyFactionRelations loads all friendly faction relations
func (da *DatabaseAdapter) LoadFriendlyFactionRelations() ([]*FactionRelation, error) {
	conn, err := da.pool.Take(context.Background())
	if err != nil {
		return nil, fmt.Errorf("failed to get connection: %w", err)
	}
	defer da.pool.Put(conn)

	var relations []*FactionRelation
	err = sqlitex.Execute(conn, "SELECT faction_id, related_faction_id FROM faction_relations WHERE is_hostile = 0", &sqlitex.ExecOptions{
		ResultFunc: func(stmt *sqlite.Stmt) error {
			relation := &FactionRelation{
				FactionID:         int32(stmt.ColumnInt64(0)),
				FriendlyFactionID: int32(stmt.ColumnInt64(1)),
			}
			relations = append(relations, relation)
			return nil
		},
	})

	if err != nil {
		return nil, fmt.Errorf("failed to load friendly faction relations: %w", err)
	}

	return relations, nil
}

// SaveFactionRelation saves a faction relation to the database
func (da *DatabaseAdapter) SaveFactionRelation(relation *FactionRelation) error {
	if relation == nil {
		return fmt.Errorf("faction relation is nil")
	}

	conn, err := da.pool.Take(context.Background())
	if err != nil {
		return fmt.Errorf("failed to get connection: %w", err)
	}
	defer da.pool.Put(conn)

	var relatedFactionID int32
	var isHostile int
	
	if relation.HostileFactionID != 0 {
		relatedFactionID = relation.HostileFactionID
		isHostile = 1
	} else if relation.FriendlyFactionID != 0 {
		relatedFactionID = relation.FriendlyFactionID
		isHostile = 0
	} else {
		return fmt.Errorf("faction relation has no related faction ID")
	}

	err = sqlitex.Execute(conn, `
		INSERT OR REPLACE INTO faction_relations (faction_id, related_faction_id, is_hostile)
		VALUES (?, ?, ?)
	`, &sqlitex.ExecOptions{
		Args: []any{relation.FactionID, relatedFactionID, isHostile},
	})

	if err != nil {
		return fmt.Errorf("failed to save faction relation %d -> %d: %w", 
			relation.FactionID, relatedFactionID, err)
	}

	return nil
}

// DeleteFactionRelation deletes a faction relation from the database
func (da *DatabaseAdapter) DeleteFactionRelation(factionID, relatedFactionID int32, isHostile bool) error {
	conn, err := da.pool.Take(context.Background())
	if err != nil {
		return fmt.Errorf("failed to get connection: %w", err)
	}
	defer da.pool.Put(conn)

	hostileFlag := 0
	if isHostile {
		hostileFlag = 1
	}

	err = sqlitex.Execute(conn, "DELETE FROM faction_relations WHERE faction_id = ? AND related_faction_id = ? AND is_hostile = ?", &sqlitex.ExecOptions{
		Args: []any{factionID, relatedFactionID, hostileFlag},
	})
	
	if err != nil {
		return fmt.Errorf("failed to delete faction relation %d -> %d: %w", 
			factionID, relatedFactionID, err)
	}

	return nil
}

// LoadPlayerFactions loads player faction values from the database
func (da *DatabaseAdapter) LoadPlayerFactions(playerID int32) (map[int32]int32, error) {
	conn, err := da.pool.Take(context.Background())
	if err != nil {
		return nil, fmt.Errorf("failed to get connection: %w", err)
	}
	defer da.pool.Put(conn)

	// Create player_factions table if it doesn't exist
	err = sqlitex.Execute(conn, `
		CREATE TABLE IF NOT EXISTS player_factions (
			player_id INTEGER NOT NULL,
			faction_id INTEGER NOT NULL,
			faction_value INTEGER NOT NULL DEFAULT 0,
			updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
			PRIMARY KEY (player_id, faction_id),
			FOREIGN KEY (faction_id) REFERENCES factions(id)
		)
	`, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create player_factions table: %w", err)
	}

	factionValues := make(map[int32]int32)
	err = sqlitex.Execute(conn, "SELECT faction_id, faction_value FROM player_factions WHERE player_id = ?", &sqlitex.ExecOptions{
		Args: []any{playerID},
		ResultFunc: func(stmt *sqlite.Stmt) error {
			factionID := int32(stmt.ColumnInt64(0))
			factionValue := int32(stmt.ColumnInt64(1))
			factionValues[factionID] = factionValue
			return nil
		},
	})

	if err != nil {
		return nil, fmt.Errorf("failed to load player factions for player %d: %w", playerID, err)
	}

	return factionValues, nil
}

// SavePlayerFaction saves a player's faction value to the database
func (da *DatabaseAdapter) SavePlayerFaction(playerID, factionID, factionValue int32) error {
	conn, err := da.pool.Take(context.Background())
	if err != nil {
		return fmt.Errorf("failed to get connection: %w", err)
	}
	defer da.pool.Put(conn)

	err = sqlitex.Execute(conn, `
		INSERT OR REPLACE INTO player_factions (player_id, faction_id, faction_value, updated_at)
		VALUES (?, ?, ?, ?)
	`, &sqlitex.ExecOptions{
		Args: []any{playerID, factionID, factionValue, time.Now().Unix()},
	})

	if err != nil {
		return fmt.Errorf("failed to save player faction %d/%d: %w", playerID, factionID, err)
	}

	return nil
}

// SaveAllPlayerFactions saves all faction values for a player
func (da *DatabaseAdapter) SaveAllPlayerFactions(playerID int32, factionValues map[int32]int32) error {
	conn, err := da.pool.Take(context.Background())
	if err != nil {
		return fmt.Errorf("failed to get connection: %w", err)
	}
	defer da.pool.Put(conn)

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

	// Clear existing faction values for this player
	err = sqlitex.Execute(conn, "DELETE FROM player_factions WHERE player_id = ?", &sqlitex.ExecOptions{
		Args: []any{playerID},
	})
	if err != nil {
		return fmt.Errorf("failed to clear player factions: %w", err)
	}

	// Insert all current faction values
	for factionID, factionValue := range factionValues {
		err = sqlitex.Execute(conn, `
			INSERT INTO player_factions (player_id, faction_id, faction_value, updated_at)
			VALUES (?, ?, ?, ?)
		`, &sqlitex.ExecOptions{
			Args: []any{playerID, factionID, factionValue, time.Now().Unix()},
		})

		if err != nil {
			return fmt.Errorf("failed to insert player faction %d/%d: %w", playerID, factionID, err)
		}
	}

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