"""F3b GET/POST/DELETE /templates 端点（内存替身，无 DB/无网络）。

覆盖：
- POST 201 + 回显 + 端点 commit；GET 列出；DELETE 204 + commit；
- 空 title → 422、空 body → 422（schema `min_length=1`，FastAPI 422）；
- 删不存在 → 404（repo.delete 返 False → AppError NOT_FOUND）。
"""

from __future__ import annotations

import os
import uuid

import httpx
import pytest
from cryptography.fernet import Fernet
from fakes_projects import FakeSession
from ww_core.domain.template_repo import TemplateCreate, TemplateView


class _FakeTemplateRepo:
    def __init__(self) -> None:
        self.rows: dict[uuid.UUID, TemplateView] = {}

    async def create(self, owner_id: uuid.UUID, data: TemplateCreate) -> TemplateView:
        view = TemplateView(
            id=uuid.uuid4(),
            title=data.title,
            body=data.body,
            category=data.category,
            tool_key=data.tool_key,
        )
        self.rows[view.id] = view
        return view

    async def list_for_owner(self, owner_id: uuid.UUID) -> list[TemplateView]:
        return list(self.rows.values())

    async def delete(self, owner_id: uuid.UUID, template_id: uuid.UUID) -> bool:
        if template_id in self.rows:
            del self.rows[template_id]
            return True
        return False


def _make_client() -> tuple[httpx.AsyncClient, _FakeTemplateRepo, FakeSession]:
    os.environ.setdefault("CREDENTIAL_ENC_KEY", Fernet.generate_key().decode())
    from ww_api.main import create_app
    from ww_api.services.project_deps import get_template_repo
    from ww_db import get_session

    repo = _FakeTemplateRepo()
    session = FakeSession()

    app = create_app()
    app.dependency_overrides[get_template_repo] = lambda: repo
    app.dependency_overrides[get_session] = lambda: session
    transport = httpx.ASGITransport(app=app)
    client = httpx.AsyncClient(transport=transport, base_url="http://test")
    return client, repo, session


@pytest.mark.asyncio
async def test_create_template_returns_201_and_commits() -> None:
    client, repo, session = _make_client()
    async with client:
        resp = await client.post(
            "/templates",
            json={"title": "爽文开局", "body": "主角穿越...", "tool_key": "expand"},
        )
    assert resp.status_code == 201
    body = resp.json()
    assert body["title"] == "爽文开局"
    assert body["body"] == "主角穿越..."
    assert body["tool_key"] == "expand"
    assert session.commits == 1
    assert len(repo.rows) == 1


@pytest.mark.asyncio
async def test_list_templates_returns_created() -> None:
    client, repo, _session = _make_client()
    async with client:
        await client.post("/templates", json={"title": "a", "body": "b"})
        resp = await client.get("/templates")
    assert resp.status_code == 200
    items = resp.json()
    assert len(items) == 1
    assert items[0]["title"] == "a"


@pytest.mark.asyncio
async def test_create_template_empty_title_returns_422() -> None:
    client, _repo, _session = _make_client()
    async with client:
        resp = await client.post("/templates", json={"title": "", "body": "b"})
    assert resp.status_code == 422


@pytest.mark.asyncio
async def test_create_template_empty_body_returns_422() -> None:
    client, _repo, _session = _make_client()
    async with client:
        resp = await client.post("/templates", json={"title": "a", "body": ""})
    assert resp.status_code == 422


@pytest.mark.asyncio
async def test_create_template_whitespace_only_title_returns_422() -> None:
    # strip 后为空 → min_length=1 不满足 → 422（防纯空白标题，审评 #2）。
    client, _repo, _session = _make_client()
    async with client:
        resp = await client.post("/templates", json={"title": "   ", "body": "b"})
    assert resp.status_code == 422


@pytest.mark.asyncio
async def test_create_template_whitespace_only_body_returns_422() -> None:
    client, _repo, _session = _make_client()
    async with client:
        resp = await client.post("/templates", json={"title": "a", "body": "   "})
    assert resp.status_code == 422


@pytest.mark.asyncio
async def test_delete_template_returns_204_and_commits() -> None:
    client, repo, session = _make_client()
    async with client:
        created = await client.post("/templates", json={"title": "a", "body": "b"})
        tid = created.json()["id"]
        resp = await client.delete(f"/templates/{tid}")
    assert resp.status_code == 204
    assert session.commits == 2
    assert repo.rows == {}


@pytest.mark.asyncio
async def test_delete_missing_template_returns_404() -> None:
    client, _repo, session = _make_client()
    async with client:
        resp = await client.delete(f"/templates/{uuid.uuid4()}")
    assert resp.status_code == 404
    assert resp.json()["error"]["code"] == "NOT_FOUND"
    assert session.commits == 0
