"""Tests for the store (persistence) module."""

import os
import tempfile
from pathlib import Path

import pytest

from mudlib.player import Player
from mudlib.store import (
    account_exists,
    authenticate,
    create_account,
    init_db,
    load_player_data,
    save_player,
)
from mudlib.zone import Zone


@pytest.fixture
def temp_db():
    """Create a temporary database for testing."""
    with tempfile.NamedTemporaryFile(delete=False, suffix=".db") as f:
        db_path = f.name

    init_db(db_path)

    yield db_path

    # Cleanup
    os.unlink(db_path)


def test_init_db_creates_file(temp_db):
    """init_db creates the database file."""
    assert Path(temp_db).exists()


def test_create_account_success(temp_db):
    """create_account creates a new account."""
    assert create_account("Alice", "password123")
    assert account_exists("Alice")


def test_create_account_case_insensitive(temp_db):
    """Account names are case-insensitive."""
    create_account("Bob", "password123")
    assert account_exists("bob")
    assert account_exists("BOB")
    assert account_exists("Bob")


def test_create_account_duplicate_fails(temp_db):
    """create_account fails if name is already taken."""
    assert create_account("Charlie", "password123")
    assert not create_account("Charlie", "different_password")
    assert not create_account("charlie", "different_password")  # case insensitive


def test_authenticate_success(temp_db):
    """authenticate returns True for correct password."""
    create_account("Dave", "correct_password")
    assert authenticate("Dave", "correct_password")


def test_authenticate_case_insensitive_name(temp_db):
    """authenticate works with case-insensitive names."""
    create_account("Eve", "password123")
    assert authenticate("eve", "password123")
    assert authenticate("EVE", "password123")


def test_authenticate_wrong_password(temp_db):
    """authenticate returns False for wrong password."""
    create_account("Frank", "correct_password")
    assert not authenticate("Frank", "wrong_password")


def test_authenticate_nonexistent_account(temp_db):
    """authenticate returns False for nonexistent account."""
    assert not authenticate("Ghost", "any_password")


def test_save_and_load_player_data(temp_db):
    """save_player and load_player_data persist player state."""
    # Create account first
    create_account("Grace", "password123")

    # Create a player with non-default values
    player = Player(
        name="Grace",
        x=42,
        y=17,
        pl=85.5,
        stamina=60.0,
        max_stamina=120.0,
        flying=True,
    )

    # Save and load
    save_player(player)
    data = load_player_data("Grace")

    assert data is not None
    assert data["x"] == 42
    assert data["y"] == 17
    assert data["pl"] == 85.5
    assert data["stamina"] == 60.0
    assert data["max_stamina"] == 120.0
    assert data["flying"] is True


def test_load_player_data_case_insensitive(temp_db):
    """load_player_data works with case-insensitive names."""
    create_account("Henry", "password123")
    player = Player(name="Henry", x=10, y=20)
    save_player(player)

    data_lower = load_player_data("henry")
    data_upper = load_player_data("HENRY")

    assert data_lower is not None
    assert data_upper is not None
    assert data_lower["x"] == 10
    assert data_upper["x"] == 10


def test_load_player_data_nonexistent(temp_db):
    """load_player_data returns None for nonexistent account."""
    assert load_player_data("Nobody") is None


def test_save_player_updates_existing(temp_db):
    """save_player updates existing player data."""
    create_account("Iris", "password123")

    # First save
    player = Player(name="Iris", x=10, y=20, pl=100.0)
    save_player(player)

    # Update and save again
    player.x = 50
    player.y = 60
    player.pl = 75.0
    save_player(player)

    # Load and verify
    data = load_player_data("Iris")
    assert data is not None
    assert data["x"] == 50
    assert data["y"] == 60
    assert data["pl"] == 75.0


def test_default_values(temp_db):
    """New accounts have default values."""
    create_account("Jack", "password123")
    data = load_player_data("Jack")

    assert data is not None
    assert data["x"] == 0
    assert data["y"] == 0
    assert data["pl"] == 100.0
    assert data["stamina"] == 100.0
    assert data["max_stamina"] == 100.0
    assert data["flying"] is False


def test_password_hashing_different_salts(temp_db):
    """Different accounts with same password have different hashes."""
    create_account("Kate", "same_password")
    create_account("Leo", "same_password")

    # Both should authenticate
    assert authenticate("Kate", "same_password")
    assert authenticate("Leo", "same_password")

    # This just verifies the API works correctly - we can't easily check
    # the hashes are different without exposing internal details


def test_save_and_load_zone_name(temp_db):
    """save_player and load_player_data persist zone_name."""
    create_account("Maria", "password123")

    # Create a zone and player
    zone = Zone(name="testzone", width=100, height=100)
    player = Player(
        name="Maria",
        location=zone,
        x=10,
        y=20,
    )

    # Save and load
    save_player(player)
    data = load_player_data("Maria")

    assert data is not None
    assert data["zone_name"] == "testzone"


def test_default_zone_name(temp_db):
    """New accounts have zone_name default to 'overworld'."""
    create_account("Noah", "password123")
    data = load_player_data("Noah")

    assert data is not None
    assert data["zone_name"] == "overworld"


def test_zone_name_migration(temp_db):
    """Existing DB without zone_name column still works."""
    # Create account, which will create default schema
    create_account("Olivia", "password123")

    # Simulate old DB by manually removing the zone_name column
    import sqlite3

    conn = sqlite3.connect(temp_db)
    cursor = conn.cursor()

    # Check if column exists and remove it
    cursor.execute("PRAGMA table_info(accounts)")
    columns = [row[1] for row in cursor.fetchall()]
    if "zone_name" in columns:
        # SQLite doesn't support DROP COLUMN directly, so recreate table
        cursor.execute("""
            CREATE TABLE accounts_backup AS
            SELECT name, password_hash, salt, x, y, pl, stamina,
                   max_stamina, flying, created_at, last_login
            FROM accounts
        """)
        cursor.execute("DROP TABLE accounts")
        cursor.execute("""
            CREATE TABLE accounts (
                name TEXT PRIMARY KEY COLLATE NOCASE,
                password_hash TEXT NOT NULL,
                salt TEXT NOT NULL,
                x INTEGER NOT NULL DEFAULT 0,
                y INTEGER NOT NULL DEFAULT 0,
                pl REAL NOT NULL DEFAULT 100.0,
                stamina REAL NOT NULL DEFAULT 100.0,
                max_stamina REAL NOT NULL DEFAULT 100.0,
                flying INTEGER NOT NULL DEFAULT 0,
                created_at TEXT NOT NULL DEFAULT (datetime('now')),
                last_login TEXT
            )
        """)
        cursor.execute("""
            INSERT INTO accounts
            SELECT * FROM accounts_backup
        """)
        cursor.execute("DROP TABLE accounts_backup")
        conn.commit()

    conn.close()

    # Now try to load player data - should handle missing column gracefully
    data = load_player_data("Olivia")
    assert data is not None
    assert data["zone_name"] == "overworld"  # Should default
