from __future__ import annotations

# pyright: reportMissingImports=false

"""
WikiAPI 단위 테스트 (FastAPI TestClient 기반)
테스터: 모리건 (개발3팀)
"""

import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient

import kakao_knowledge.wiki_api as wiki_api_module
from kakao_knowledge.wiki_api import router
from kakao_knowledge.wiki_store import WikiStore


# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------


@pytest.fixture
def client():
    """테스트용 FastAPI 클라이언트 (인메모리 DB)"""
    app = FastAPI()
    app.include_router(router)

    # 테스트용 인메모리 store로 교체
    test_store = WikiStore(db_path=":memory:")

    original_store = wiki_api_module._store
    wiki_api_module._store = test_store

    with TestClient(app) as c:
        yield c

    test_store.close()
    wiki_api_module._store = original_store


@pytest.fixture
def sample_entry():
    return {
        "id": "kakao-test-001",
        "title": "테스트 항목",
        "category": "보상",
        "question": "테스트 질문입니다",
        "answer": "테스트 답변입니다",
        "keywords": ["테스트", "보상"],
        "confidence": "high",
    }


# ---------------------------------------------------------------------------
# 헬퍼
# ---------------------------------------------------------------------------


def _create(client: TestClient, entry: dict) -> dict:
    """POST로 항목 생성 후 응답 dict 반환."""
    resp = client.post("/api/wiki/entries", json=entry)
    assert resp.status_code == 201, resp.text
    return resp.json()


# ---------------------------------------------------------------------------
# 1. POST /api/wiki/entries
# ---------------------------------------------------------------------------


class TestCreateEntry:
    def test_create_entry(self, client: TestClient, sample_entry: dict):
        """POST /api/wiki/entries는 201을 반환하고 생성된 항목의 id를 포함해야 한다."""
        resp = client.post("/api/wiki/entries", json=sample_entry)
        assert resp.status_code == 201
        data = resp.json()
        assert data["id"] == sample_entry["id"]


# ---------------------------------------------------------------------------
# 2. GET /api/wiki/entries/{id}
# ---------------------------------------------------------------------------


class TestGetEntry:
    def test_get_entry(self, client: TestClient, sample_entry: dict):
        """POST로 생성 후 GET /api/wiki/entries/{id}로 조회할 수 있어야 한다."""
        _create(client, sample_entry)
        resp = client.get(f"/api/wiki/entries/{sample_entry['id']}")
        assert resp.status_code == 200
        data = resp.json()
        assert data["id"] == sample_entry["id"]
        assert data["title"] == sample_entry["title"]
        assert data["category"] == sample_entry["category"]

    def test_get_entry_not_found(self, client: TestClient):
        """존재하지 않는 id 조회 시 404를 반환해야 한다."""
        resp = client.get("/api/wiki/entries/nonexistent-id-9999")
        assert resp.status_code == 404


# ---------------------------------------------------------------------------
# 3. GET /api/wiki/entries (목록)
# ---------------------------------------------------------------------------


class TestListEntries:
    def test_list_entries(self, client: TestClient, sample_entry: dict):
        """여러 항목 생성 후 GET /api/wiki/entries로 목록을 조회할 수 있어야 한다."""
        entries = [
            {**sample_entry, "id": f"kakao-test-{i:03d}", "title": f"항목{i}"}
            for i in range(3)
        ]
        for e in entries:
            _create(client, e)

        resp = client.get("/api/wiki/entries")
        assert resp.status_code == 200
        data = resp.json()
        # list_entries는 리스트를 직접 반환
        assert isinstance(data, list)
        assert len(data) == 3

    def test_list_entries_filter(self, client: TestClient):
        """category/status 필터 쿼리 파라미터가 정상 동작해야 한다."""
        entries = [
            {"id": "f-001", "title": "보상1", "category": "보상", "question": "q", "answer": "a"},
            {"id": "f-002", "title": "고지1", "category": "고지의무", "question": "q", "answer": "a"},
            {"id": "f-003", "title": "보상2", "category": "보상", "question": "q", "answer": "a"},
        ]
        for e in entries:
            _create(client, e)

        resp = client.get("/api/wiki/entries?category=보상")
        assert resp.status_code == 200
        data = resp.json()
        assert isinstance(data, list)
        assert len(data) == 2
        assert all(item["category"] == "보상" for item in data)


# ---------------------------------------------------------------------------
# 4. PUT /api/wiki/entries/{id}
# ---------------------------------------------------------------------------


class TestUpdateEntry:
    def test_update_entry(self, client: TestClient, sample_entry: dict):
        """PUT /api/wiki/entries/{id}로 항목을 업데이트할 수 있어야 한다."""
        _create(client, sample_entry)
        resp = client.put(
            f"/api/wiki/entries/{sample_entry['id']}",
            json={"answer": "수정된 답변"},
        )
        assert resp.status_code == 200
        data = resp.json()
        assert data["answer"] == "수정된 답변"

    def test_update_entry_not_found(self, client: TestClient):
        """존재하지 않는 id 업데이트 시 404를 반환해야 한다."""
        resp = client.put(
            "/api/wiki/entries/nonexistent-id-9999",
            json={"answer": "변경"},
        )
        assert resp.status_code == 404


# ---------------------------------------------------------------------------
# 5. DELETE /api/wiki/entries/{id}
# ---------------------------------------------------------------------------


class TestDeleteEntry:
    def test_delete_entry(self, client: TestClient, sample_entry: dict):
        """DELETE /api/wiki/entries/{id}로 항목을 삭제할 수 있어야 한다."""
        _create(client, sample_entry)
        resp = client.delete(f"/api/wiki/entries/{sample_entry['id']}")
        assert resp.status_code == 204
        # 삭제 후 GET 시 404
        resp2 = client.get(f"/api/wiki/entries/{sample_entry['id']}")
        assert resp2.status_code == 404

    def test_delete_entry_not_found(self, client: TestClient):
        """존재하지 않는 id 삭제 시 404를 반환해야 한다."""
        resp = client.delete("/api/wiki/entries/nonexistent-id-9999")
        assert resp.status_code == 404


# ---------------------------------------------------------------------------
# 6. POST /api/wiki/entries/{id}/approve
# ---------------------------------------------------------------------------


class TestApproveEntry:
    def test_approve_entry(self, client: TestClient, sample_entry: dict):
        """POST /api/wiki/entries/{id}/approve 후 status가 approved가 되어야 한다."""
        _create(client, sample_entry)
        resp = client.post(f"/api/wiki/entries/{sample_entry['id']}/approve")
        assert resp.status_code == 200
        data = resp.json()
        assert data["status"] == "approved"


# ---------------------------------------------------------------------------
# 7. POST /api/wiki/entries/{id}/reject
# ---------------------------------------------------------------------------


class TestRejectEntry:
    def test_reject_entry(self, client: TestClient, sample_entry: dict):
        """POST /api/wiki/entries/{id}/reject 후 status가 rejected가 되어야 한다."""
        _create(client, sample_entry)
        resp = client.post(f"/api/wiki/entries/{sample_entry['id']}/reject")
        assert resp.status_code == 200
        data = resp.json()
        assert data["status"] == "rejected"


# ---------------------------------------------------------------------------
# 8. GET /api/wiki/search
# ---------------------------------------------------------------------------


class TestSearchEntries:
    def test_search_entries(self, client: TestClient, sample_entry: dict):
        """GET /api/wiki/search?q=테스트 로 항목을 검색할 수 있어야 한다."""
        _create(client, sample_entry)
        resp = client.get("/api/wiki/search?q=테스트")
        assert resp.status_code == 200
        data = resp.json()
        # search_entries는 리스트를 직접 반환
        assert isinstance(data, list)
        ids = [item["id"] for item in data]
        assert sample_entry["id"] in ids


# ---------------------------------------------------------------------------
# 9. GET /api/wiki/stats
# ---------------------------------------------------------------------------


class TestStats:
    def test_stats(self, client: TestClient, sample_entry: dict):
        """GET /api/wiki/stats 는 total/by_category/by_status를 반환해야 한다."""
        _create(client, sample_entry)
        resp = client.get("/api/wiki/stats")
        assert resp.status_code == 200
        data = resp.json()
        assert "total" in data
        assert "by_category" in data
        assert "by_status" in data
        assert data["total"] >= 1
        assert data["by_category"].get("보상", 0) >= 1


# ---------------------------------------------------------------------------
# 10. POST /api/wiki/import
# ---------------------------------------------------------------------------


class TestBulkImport:
    def test_bulk_import(self, client: TestClient):
        """POST /api/wiki/import로 여러 항목을 일괄 import 할 수 있어야 한다."""
        entries = [
            {"id": f"import-{i:03d}", "title": f"import 항목{i}", "category": "테스트", "question": f"q{i}", "answer": f"a{i}"}
            for i in range(3)
        ]
        # bulk_import는 body가 list[dict] 직접 전달
        resp = client.post("/api/wiki/import", json=entries)
        assert resp.status_code == 200
        data = resp.json()
        assert data["imported"] == 3
        assert data["total"] == 3
