package db

import (
	"crypto/sha256"
	"database/sql"
	"encoding/hex"
	"errors"
	"fmt"
	"sync"
	"time"

	_ "github.com/mattn/go-sqlite3"
)

var db *sql.DB

var (
	ErrUserExists         = errors.New("username already exists")
	ErrInvalidCredentials = errors.New("invalid username or password")
)

const (
	maxRegistrationsPerIP = 3              // Maximum registrations allowed per IP
	registrationWindow    = 24 * time.Hour // Time window for rate limiting
)

type registrationAttempt struct {
	count    int
	firstTry time.Time
}

var (
	registrationAttempts = make(map[string]*registrationAttempt)
	rateLimitMutex       sync.RWMutex
)

func CleanupOldAttempts() {
	rateLimitMutex.Lock()
	defer rateLimitMutex.Unlock()

	now := time.Now()
	for ip, attempt := range registrationAttempts {
		if now.Sub(attempt.firstTry) > registrationWindow {
			delete(registrationAttempts, ip)
		}
	}
}

func CheckRegistrationLimit(ip string) error {
	rateLimitMutex.Lock()
	defer rateLimitMutex.Unlock()

	now := time.Now()
	attempt, exists := registrationAttempts[ip]

	if !exists {
		registrationAttempts[ip] = &registrationAttempt{
			count:    1,
			firstTry: now,
		}
		return nil
	}

	// Reset if window has passed
	if now.Sub(attempt.firstTry) > registrationWindow {
		attempt.count = 1
		attempt.firstTry = now
		return nil
	}

	if attempt.count >= maxRegistrationsPerIP {
		return fmt.Errorf("registration limit reached for this IP. Please try again in %v",
			registrationWindow-now.Sub(attempt.firstTry))
	}

	attempt.count++
	return nil
}

func InitDB(dbPath string) error {
	var err error
	db, err = sql.Open("sqlite3", dbPath)
	if err != nil {
		return err
	}

	// Create tables if they don't exist
	_, err = db.Exec(`
		CREATE TABLE IF NOT EXISTS players (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			username TEXT UNIQUE NOT NULL,
			password_hash TEXT NOT NULL,
			created_at DATETIME NOT NULL
		);

		CREATE TABLE IF NOT EXISTS player_states (
			player_id INTEGER PRIMARY KEY,
			x INTEGER NOT NULL,
			y INTEGER NOT NULL,
			last_seen DATETIME NOT NULL,
			FOREIGN KEY(player_id) REFERENCES players(id)
		);
	`)
	return err
}

func hashPassword(password string) string {
	hash := sha256.Sum256([]byte(password))
	return hex.EncodeToString(hash[:])
}

func RegisterPlayer(username, password string) (int, error) {
	// Check if username exists
	var exists bool
	err := db.QueryRow("SELECT EXISTS(SELECT 1 FROM players WHERE username = ?)", username).Scan(&exists)
	if err != nil {
		return 0, err
	}
	if exists {
		return 0, ErrUserExists
	}

	// Create new player
	result, err := db.Exec(`
		INSERT INTO players (username, password_hash, created_at)
		VALUES (?, ?, ?)`,
		username, hashPassword(password), time.Now().UTC(),
	)
	if err != nil {
		return 0, err
	}

	id, err := result.LastInsertId()
	return int(id), err
}

func AuthenticatePlayer(username, password string) (int, error) {
	var id int
	var storedHash string
	err := db.QueryRow(`
		SELECT id, password_hash 
		FROM players 
		WHERE username = ?`,
		username,
	).Scan(&id, &storedHash)

	if err == sql.ErrNoRows {
		return 0, ErrInvalidCredentials
	}
	if err != nil {
		return 0, err
	}

	if storedHash != hashPassword(password) {
		return 0, ErrInvalidCredentials
	}

	return id, nil
}

func SavePlayerState(playerID int, x, y int) error {
	_, err := db.Exec(`
		INSERT OR REPLACE INTO player_states (
			player_id, x, y, last_seen
		) VALUES (?, ?, ?, ?)`,
		playerID, x, y, time.Now().UTC(),
	)
	return err
}

func LoadPlayerState(playerID int) (x, y int, err error) {
	err = db.QueryRow(`
		SELECT x, y FROM player_states 
		WHERE player_id = ?`,
		playerID,
	).Scan(&x, &y)
	if err == sql.ErrNoRows {
		// Return default position for new players
		return 5, 5, nil
	}
	return x, y, err
}

func GetUsername(playerID int) (string, error) {
	var username string
	err := db.QueryRow("SELECT username FROM players WHERE id = ?", playerID).Scan(&username)
	return username, err
}
