import pytest
import json
from pathlib import Path

from dracula.memory import Memory, AsyncMemory
from dracula.stats import Stats, AsyncStats
from dracula.exceptions import ChatException

# ==========================================
# 1. Tests for synchronous Memory class
# ==========================================


def test_memory_add_and_get_history(tmp_path):
    """Test adding messages and retrieving them chronologically."""
    db_file = tmp_path / "memory.db"
    memory = Memory(db_path=db_file, max_messages=5)

    memory.add_message("user", "Hello")
    memory.add_message("model", "Hi there!")

    history = memory.get_history()
    assert len(history) == 2
    assert history[0] == {"role": "user", "content": "Hello"}
    assert history[1] == {"role": "model", "content": "Hi there!"}


def test_memory_trim_history(tmp_path):
    """Test that history is trimmed correctly when exceeding max_messages."""
    db_file = tmp_path / "memory.db"
    memory = Memory(db_path=db_file, max_messages=3)

    for i in range(5):
        memory.add_message("user", f"Message {i}")

    history = memory.get_history()

    assert len(history) == 3
    assert history[0]["content"] == "Message 2"
    assert history[-1]["content"] == "Message 4"


def test_memory_clear(tmp_path):
    """Test permanently deleting the conversation history."""
    db_file = tmp_path / "memory.db"
    memory = Memory(db_path=db_file)

    memory.add_message("user", "Hello")
    assert len(memory.get_history()) == 1

    memory.clear()
    assert len(memory.get_history()) == 0


def test_memory_save_and_load(tmp_path):
    """Test exporting to JSON and migrating back to SQLite."""
    db_file = tmp_path / "memory.db"
    json_file = tmp_path / "backup.json"
    memory = Memory(db_path=db_file)

    memory.add_message("user", "Test message")
    memory.save(filepath=str(json_file))

    assert json_file.exists()

    db_file2 = tmp_path / "memory2.db"
    memory2 = Memory(db_path=db_file2)
    memory2.load(filepath=str(json_file))

    history = memory2.get_history()
    assert len(history) == 1
    assert history[0]["content"] == "Test message"


# ==========================================
# 2. Tests for synchronous Stats class
# ==========================================


def test_stats_initialization(tmp_path):
    """Test if stats are initialized to zero."""
    db_file = tmp_path / "stats.db"
    stats = Stats(db_path=db_file)

    data = stats.get_stats()
    assert data["total_messages"] == 0
    assert data["total_characters_sent"] == 0


def test_stats_recording_and_reset(tmp_path):
    """Test recording messages/responses and resetting."""
    db_file = tmp_path / "stats.db"
    stats = Stats(db_path=db_file)

    stats.record_message("Hello")
    stats.record_response("Hi there!")

    data = stats.get_stats()
    assert data["total_messages"] == 1
    assert data["total_characters_sent"] == 5
    assert data["total_responses"] == 1
    assert data["total_characters_received"] == 9

    stats.reset()
    reset_data = stats.get_stats()
    assert reset_data["total_messages"] == 0
    assert reset_data["total_responses"] == 0


# ==========================================
# 3. Tests for AsyncMemory class
# ==========================================


@pytest.mark.asyncio
async def test_async_memory_add_get_clear(tmp_path):
    """Test async operations for conversation history."""
    db_file = tmp_path / "async_memory.db"
    memory = AsyncMemory(db_path=db_file, max_messages=2)
    await memory.initialize()

    await memory.add_message("user", "Async Hello")
    await memory.add_message("model", "Async Hi")
    await memory.add_message("user", "Overflow message")

    history = await memory.get_history()
    assert len(history) == 2

    assert history[0]["content"] == "Async Hi"

    await memory.clear()
    empty_history = await memory.get_history()
    assert len(empty_history) == 0


# ==========================================
# 4. Tests for AsyncStats class
# ==========================================


@pytest.mark.asyncio
async def test_async_stats_record_and_reset(tmp_path):
    """Test async operations for usage tracking."""
    db_file = tmp_path / "async_stats.db"
    stats = AsyncStats(db_path=db_file)
    await stats.initialize()

    await stats.record_message("Hi")
    await stats.record_response("Hello!")

    data = await stats.get_stats()
    assert data["total_messages"] == 1
    assert data["total_characters_sent"] == 2
    assert data["total_responses"] == 1
    assert data["total_characters_received"] == 6

    await stats.reset()
    reset_data = await stats.get_stats()
    assert reset_data["total_messages"] == 0
    assert reset_data["total_characters_received"] == 0
