from typing import Any

from httpx import AsyncClient
from sqlalchemy.ext.asyncio import AsyncSession

from src.models import Category, Difficulty, Explanation, Pattern, Question, Solution

SampleData = dict[str, Any]


async def create_sample_data(db_session: AsyncSession) -> SampleData:
    category = Category(name="Arrays", slug="arrays", description="Array problems")
    db_session.add(category)

    pattern = Pattern(
        name="Two Pointers",
        slug="two-pointers",
        description="Two pointer technique",
    )
    db_session.add(pattern)

    await db_session.flush()

    question = Question(
        title="Two Sum",
        slug="two-sum",
        difficulty=Difficulty.EASY,
        description="Find two numbers that add up to target.",
        leetcode_id=1,
        leetcode_url="https://leetcode.com/problems/two-sum/",
    )
    question.categories = [category]
    question.patterns = [pattern]
    db_session.add(question)

    await db_session.flush()

    explanation = Explanation(
        question_id=question.id,
        approach="Use a hash map.",
        intuition="Store seen values for O(1) lookup.",
        time_complexity="O(n)",
        space_complexity="O(n)",
    )
    db_session.add(explanation)

    solution = Solution(
        question_id=question.id,
        approach_name="Hash Map",
        code="def two_sum(nums, target): pass",
        is_optimal=True,
    )
    db_session.add(solution)

    await db_session.commit()

    return {"question": question, "category": category, "pattern": pattern}


async def test_health_check(client: AsyncClient) -> None:
    response = await client.get("/api/health")
    assert response.status_code == 200
    assert response.json() == {"status": "healthy"}


async def test_list_questions_empty(client: AsyncClient) -> None:
    response = await client.get("/api/questions")
    assert response.status_code == 200
    data = response.json()
    assert data["items"] == []
    assert data["total"] == 0


async def test_list_questions(client: AsyncClient, db_session: AsyncSession) -> None:
    await create_sample_data(db_session)
    response = await client.get("/api/questions")
    assert response.status_code == 200
    data = response.json()
    assert data["total"] == 1
    assert len(data["items"]) == 1
    assert data["items"][0]["title"] == "Two Sum"
    assert data["items"][0]["difficulty"] == "easy"


async def test_list_questions_filter_difficulty(
    client: AsyncClient, db_session: AsyncSession
) -> None:
    await create_sample_data(db_session)
    response = await client.get("/api/questions?difficulty=easy")
    assert response.status_code == 200
    assert response.json()["total"] == 1

    response = await client.get("/api/questions?difficulty=hard")
    assert response.status_code == 200
    assert response.json()["total"] == 0


async def test_list_questions_invalid_difficulty(client: AsyncClient) -> None:
    response = await client.get("/api/questions?difficulty=invalid")
    assert response.status_code == 400


async def test_get_question(client: AsyncClient, db_session: AsyncSession) -> None:
    await create_sample_data(db_session)
    response = await client.get("/api/questions/two-sum")
    assert response.status_code == 200
    data = response.json()
    assert data["title"] == "Two Sum"
    assert data["explanation"]["time_complexity"] == "O(n)"
    assert len(data["solutions"]) == 1


async def test_get_question_not_found(client: AsyncClient) -> None:
    response = await client.get("/api/questions/nonexistent")
    assert response.status_code == 404


async def test_list_categories_empty(client: AsyncClient) -> None:
    response = await client.get("/api/categories")
    assert response.status_code == 200
    assert response.json()["items"] == []


async def test_list_categories(client: AsyncClient, db_session: AsyncSession) -> None:
    await create_sample_data(db_session)
    response = await client.get("/api/categories")
    assert response.status_code == 200
    data = response.json()
    assert len(data["items"]) == 1
    assert data["items"][0]["name"] == "Arrays"
    assert data["items"][0]["question_count"] == 1


async def test_list_patterns_empty(client: AsyncClient) -> None:
    response = await client.get("/api/patterns")
    assert response.status_code == 200
    assert response.json()["items"] == []


async def test_list_patterns(client: AsyncClient, db_session: AsyncSession) -> None:
    await create_sample_data(db_session)
    response = await client.get("/api/patterns")
    assert response.status_code == 200
    data = response.json()
    assert len(data["items"]) == 1
    assert data["items"][0]["name"] == "Two Pointers"


async def test_get_pattern(client: AsyncClient, db_session: AsyncSession) -> None:
    await create_sample_data(db_session)
    response = await client.get("/api/patterns/two-pointers")
    assert response.status_code == 200
    data = response.json()
    assert data["name"] == "Two Pointers"
    assert data["question_count"] == 1


async def test_get_pattern_not_found(client: AsyncClient) -> None:
    response = await client.get("/api/patterns/nonexistent")
    assert response.status_code == 404


async def test_get_stats_empty(client: AsyncClient) -> None:
    response = await client.get("/api/stats")
    assert response.status_code == 200
    data = response.json()
    assert data["total_questions"] == 0
    assert data["by_difficulty"]["easy"] == 0


async def test_get_stats(client: AsyncClient, db_session: AsyncSession) -> None:
    await create_sample_data(db_session)
    response = await client.get("/api/stats")
    assert response.status_code == 200
    data = response.json()
    assert data["total_questions"] == 1
    assert data["by_difficulty"]["easy"] == 1
    assert len(data["by_category"]) == 1
    assert len(data["by_pattern"]) == 1
