package main

import (
	"eq2emu/internal/database"
	"fmt"
	"log"
	"time"

	"golang.org/x/crypto/bcrypt"
)

// Database handles all database operations for the login server
type Database struct {
	db *database.DB
}

// DatabaseConfig holds database connection settings
type DatabaseConfig struct {
	FilePath       string `json:"file_path"`
	MaxConnections int    `json:"max_connections"`
	BusyTimeout    int    `json:"busy_timeout_ms"`
}

// NewDatabase creates a new database connection
func NewDatabase(config DatabaseConfig) (*Database, error) {
	db, err := database.Open(config.FilePath)
	if err != nil {
		return nil, fmt.Errorf("failed to open database: %w", err)
	}

	// Set busy timeout if specified
	if config.BusyTimeout > 0 {
		query := fmt.Sprintf("PRAGMA busy_timeout = %d", config.BusyTimeout)
		if err := db.Exec(query); err != nil {
			return nil, fmt.Errorf("failed to set busy timeout: %w", err)
		}
	}

	log.Println("SQLite database connection established")
	return &Database{db: db}, nil
}

// Close closes the database connection
func (d *Database) Close() error {
	return d.db.Close()
}

// AuthenticateAccount verifies user credentials and returns account info
func (d *Database) AuthenticateAccount(username, password string) (*Account, error) {
	query := `
		SELECT id, username, password_hash, ls_admin, world_admin,
		       created_date, last_login, client_version
		FROM accounts
		WHERE username = ? AND active = 1`

	row, err := d.db.QueryRow(query, username)
	if err != nil {
		return nil, fmt.Errorf("query failed: %w", err)
	}
	if row == nil {
		return nil, nil // Account not found
	}
	defer row.Close()

	var account Account
	account.ID = int32(row.Int64(0))
	account.Username = row.Text(1)
	passwordHash := row.Text(2)
	account.LSAdmin = row.Bool(3)
	account.WorldAdmin = row.Bool(4)
	// Skip created_date at index 5 - not needed for authentication
	lastLogin := row.Text(6)
	account.ClientVersion = uint16(row.Int64(7))

	// Verify password
	if err := bcrypt.CompareHashAndPassword([]byte(passwordHash), []byte(password)); err != nil {
		return nil, nil // Invalid password
	}

	// Parse timestamp
	if lastLogin != "" {
		if t, err := time.Parse("2006-01-02 15:04:05", lastLogin); err == nil {
			account.LastLogin = t
		}
	}

	return &account, nil
}

// UpdateAccountLogin updates account login timestamp and IP
func (d *Database) UpdateAccountLogin(account *Account) error {
	query := `
		UPDATE accounts
		SET last_login = ?, last_ip = ?, client_version = ?
		WHERE id = ?`

	return d.db.Exec(query,
		account.LastLogin.Format("2006-01-02 15:04:05"),
		account.IPAddress,
		account.ClientVersion,
		account.ID,
	)
}

// LoadCharacters loads all characters for an account
func (d *Database) LoadCharacters(accountID int32, version uint16) ([]*Character, error) {
	query := `
		SELECT id, server_id, name, level, race, gender, class,
		       created_date, deleted
		FROM characters
		WHERE account_id = ?
		ORDER BY created_date ASC`

	var characters []*Character
	err := d.db.Query(query, func(row *database.Row) error {
		char := &Character{AccountID: accountID}
		char.ID = int32(row.Int64(0))
		char.ServerID = int32(row.Int64(1))
		char.Name = row.Text(2)
		char.Level = int8(row.Int(3))
		char.Race = int8(row.Int(4))
		char.Gender = int8(row.Int(5))
		char.Class = int8(row.Int(6))

		if dateStr := row.Text(7); dateStr != "" {
			if t, err := time.Parse("2006-01-02 15:04:05", dateStr); err == nil {
				char.CreatedDate = t
			}
		}

		char.Deleted = row.Bool(8)
		characters = append(characters, char)
		return nil
	}, accountID)

	return characters, err
}

// CharacterNameExists checks if a character name is already taken
func (d *Database) CharacterNameExists(name string, serverID int32) (bool, error) {
	query := `
		SELECT COUNT(*)
		FROM characters
		WHERE name = ? AND server_id = ? AND deleted = 0`

	row, err := d.db.QueryRow(query, name, serverID)
	if err != nil {
		return false, err
	}
	if row == nil {
		return false, nil
	}
	defer row.Close()

	return row.Int(0) > 0, nil
}

// CreateCharacter creates a new character in the database
func (d *Database) CreateCharacter(char *Character) (int32, error) {
	query := `
		INSERT INTO characters (account_id, server_id, name, level, race,
		                       gender, class, created_date)
		VALUES (?, ?, ?, ?, ?, ?, ?, ?)`

	err := d.db.Exec(query,
		char.AccountID, char.ServerID, char.Name, char.Level,
		char.Race, char.Gender, char.Class,
		char.CreatedDate.Format("2006-01-02 15:04:05"),
	)
	if err != nil {
		return 0, fmt.Errorf("failed to create character: %w", err)
	}

	id := int32(d.db.LastInsertID())
	log.Printf("Created character %s (ID: %d) for account %d",
		char.Name, id, char.AccountID)

	return id, nil
}

// DeleteCharacter marks a character as deleted
func (d *Database) DeleteCharacter(charID, accountID int32) error {
	query := `
		UPDATE characters
		SET deleted = 1, deleted_date = CURRENT_TIMESTAMP
		WHERE id = ? AND account_id = ?`

	err := d.db.Exec(query, charID, accountID)
	if err != nil {
		return fmt.Errorf("failed to delete character: %w", err)
	}

	// Check if any rows were affected
	if d.db.Changes() == 0 {
		return fmt.Errorf("character not found or not owned by account")
	}

	log.Printf("Deleted character %d for account %d", charID, accountID)
	return nil
}

// GetWorldServers returns all configured world servers
func (d *Database) GetWorldServers() ([]*WorldServer, error) {
	query := `
		SELECT id, name, description, ip_address, port, status,
		       population, locked, hidden, created_date
		FROM world_servers
		WHERE active = 1
		ORDER BY sort_order, name`

	var servers []*WorldServer
	err := d.db.Query(query, func(row *database.Row) error {
		server := &WorldServer{}
		server.ID = int32(row.Int64(0))
		server.Name = row.Text(1)
		server.Description = row.Text(2)
		server.IPAddress = row.Text(3)
		server.Port = row.Int(4)
		server.Status = row.Text(5)
		server.Population = int32(row.Int64(6))
		server.Locked = row.Bool(7)
		server.Hidden = row.Bool(8)

		if dateStr := row.Text(9); dateStr != "" {
			if t, err := time.Parse("2006-01-02 15:04:05", dateStr); err == nil {
				server.CreatedDate = t
			}
		}

		server.Online = server.Status == "online"
		server.PopulationLevel = calculatePopulationLevel(server.Population)
		servers = append(servers, server)
		return nil
	})

	return servers, err
}

// UpdateWorldServerStats updates world server statistics
func (d *Database) UpdateWorldServerStats(serverID int32, stats *WorldServerStats) error {
	query := `
		INSERT OR REPLACE INTO world_server_stats
		(server_id, timestamp, population, zones_active, players_online, uptime_seconds)
		VALUES (?, CURRENT_TIMESTAMP, ?, ?, ?, ?)`

	return d.db.Exec(query,
		serverID, stats.Population,
		stats.ZonesActive, stats.PlayersOnline, stats.UptimeSeconds,
	)
}

// CleanupOldEntries removes old log entries and statistics
func (d *Database) CleanupOldEntries() error {
	queries := []string{
		"DELETE FROM login_attempts WHERE timestamp < datetime('now', '-30 days')",
		"DELETE FROM world_server_stats WHERE timestamp < datetime('now', '-7 days')",
		"DELETE FROM client_logs WHERE timestamp < datetime('now', '-14 days')",
	}

	for _, query := range queries {
		if err := d.db.Exec(query); err != nil {
			log.Printf("Cleanup query failed: %v", err)
		}
	}

	return nil
}

// LogLoginAttempt records a login attempt for security monitoring
func (d *Database) LogLoginAttempt(username, ipAddress string, success bool) error {
	query := `
		INSERT INTO login_attempts (username, ip_address, success, timestamp)
		VALUES (?, ?, ?, CURRENT_TIMESTAMP)`

	return d.db.Exec(query, username, ipAddress, success)
}

// GetMaxCharsSetting returns the maximum characters per account
func (d *Database) GetMaxCharsSetting() int32 {
	var maxChars int32 = 7 // Default

	query := "SELECT value FROM server_settings WHERE name = 'max_characters_per_account'"
	row, err := d.db.QueryRow(query)
	if err != nil || row == nil {
		return maxChars
	}
	defer row.Close()

	if !row.IsNull(0) {
		if val := row.Int64(0); val > 0 {
			maxChars = int32(val)
		}
	}

	return maxChars
}

// GetAccountBonus returns veteran bonus flags for an account
func (d *Database) GetAccountBonus(accountID int32) uint8 {
	var bonus uint8 = 0

	query := "SELECT veteran_bonus FROM accounts WHERE id = ?"
	row, err := d.db.QueryRow(query, accountID)
	if err != nil || row == nil {
		return bonus
	}
	defer row.Close()

	bonus = uint8(row.Int(0))
	return bonus
}

// calculatePopulationLevel converts population to display level
func calculatePopulationLevel(population int32) uint8 {
	switch {
	case population >= 1000:
		return 3 // Full
	case population >= 500:
		return 2 // High
	case population >= 100:
		return 1 // Medium
	default:
		return 0 // Low
	}
}
