"""Tests for /api/v1/documents routes (U7)."""

from __future__ import annotations

import asyncio
from pathlib import Path

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

from agentkit.documents.db import init_documents_db
from agentkit.documents.renderers.excel_renderer import ExcelRenderer
from agentkit.documents.renderers.pdf_renderer import PDFRenderer
from agentkit.documents.renderers.word_renderer import WordRenderer
from agentkit.documents.service import DocumentService
from agentkit.server.routes import documents as documents_routes


@pytest.fixture
def app(tmp_path: Path) -> FastAPI:
    """Create a test app with DocumentService initialized."""
    db_path = tmp_path / "test.db"
    upload_dir = tmp_path / "uploads"
    asyncio.run(init_documents_db(db_path))

    service = DocumentService(upload_dir=upload_dir, db_path=db_path)
    service.register_renderer("word", WordRenderer())
    service.register_renderer("excel", ExcelRenderer())
    service.register_renderer("pdf", PDFRenderer())

    app = FastAPI()
    app.state.document_service = service
    app.state.server_config = None  # No API key configured → allow all
    app.include_router(documents_routes.router, prefix="/api/v1")
    return app


@pytest.fixture
def client(app: FastAPI) -> TestClient:
    return TestClient(app)


# ---------------------------------------------------------------------------
# POST /create
# ---------------------------------------------------------------------------


def test_create_word(client: TestClient) -> None:
    """POST /create with format=word returns 200 + document metadata."""
    resp = client.post(
        "/api/v1/documents/create",
        json={
            "format": "word",
            "content": "# Test\n\nParagraph.",
            "conversation_id": "conv-1",
        },
    )
    assert resp.status_code == 200
    data = resp.json()
    assert data["success"] is True
    assert data["document"]["format"] == "word"
    assert data["document"]["filename"].endswith(".docx")
    assert data["document"]["download_url"].startswith("/api/v1/documents/download/")


def test_create_pdf(client: TestClient) -> None:
    """POST /create with format=pdf returns 200."""
    resp = client.post(
        "/api/v1/documents/create",
        json={
            "format": "pdf",
            "content": "# PDF Test",
            "conversation_id": "conv-1",
        },
    )
    assert resp.status_code == 200
    assert resp.json()["document"]["format"] == "pdf"


def test_create_excel_json(client: TestClient) -> None:
    """POST /create with format=excel and JSON content returns 200."""
    resp = client.post(
        "/api/v1/documents/create",
        json={
            "format": "excel",
            "content": '{"Data": [["A", "B"], ["1", "2"]]}',
            "conversation_id": "conv-1",
        },
    )
    assert resp.status_code == 200
    assert resp.json()["document"]["format"] == "excel"


def test_create_invalid_format(client: TestClient) -> None:
    """POST /create with invalid format returns 400."""
    resp = client.post(
        "/api/v1/documents/create",
        json={
            "format": "pptx",
            "content": "test",
            "conversation_id": "conv-1",
        },
    )
    assert resp.status_code == 400


def test_create_missing_fields(client: TestClient) -> None:
    """POST /create with missing required fields returns 422."""
    resp = client.post(
        "/api/v1/documents/create",
        json={"format": "word"},
    )
    assert resp.status_code == 422  # Pydantic validation error


# ---------------------------------------------------------------------------
# GET /conversation/{id}
# ---------------------------------------------------------------------------


def test_list_conversation_documents(client: TestClient) -> None:
    """GET /conversation/{id} returns documents for that conversation."""
    # Create a document first
    client.post(
        "/api/v1/documents/create",
        json={
            "format": "word",
            "content": "# Doc 1",
            "conversation_id": "conv-list",
        },
    )
    client.post(
        "/api/v1/documents/create",
        json={
            "format": "pdf",
            "content": "# Doc 2",
            "conversation_id": "conv-list",
        },
    )

    resp = client.get("/api/v1/documents/conversation/conv-list")
    assert resp.status_code == 200
    data = resp.json()
    assert data["success"] is True
    assert data["count"] == 2
    assert data["conversation_id"] == "conv-list"
    formats = [d["format"] for d in data["documents"]]
    assert "word" in formats
    assert "pdf" in formats


def test_list_empty_conversation(client: TestClient) -> None:
    """GET /conversation/{id} with no documents returns empty list."""
    resp = client.get("/api/v1/documents/conversation/no-such-conv")
    assert resp.status_code == 200
    data = resp.json()
    assert data["count"] == 0
    assert data["documents"] == []


# ---------------------------------------------------------------------------
# GET /download/{doc_id}
# ---------------------------------------------------------------------------


def test_download_document(client: TestClient) -> None:
    """GET /download/{doc_id} returns the file."""
    # Create a document
    create_resp = client.post(
        "/api/v1/documents/create",
        json={
            "format": "word",
            "content": "# Downloadable",
            "conversation_id": "conv-dl",
        },
    )
    doc_id = create_resp.json()["document"]["id"]

    # Download it
    resp = client.get(f"/api/v1/documents/download/{doc_id}")
    assert resp.status_code == 200
    assert resp.headers["content-type"] == "application/octet-stream"
    assert len(resp.content) > 0


def test_download_not_found(client: TestClient) -> None:
    """GET /download/{nonexistent} returns 404."""
    resp = client.get("/api/v1/documents/download/nonexistent-id")
    assert resp.status_code == 404


# ---------------------------------------------------------------------------
# POST /upload-template
# ---------------------------------------------------------------------------


def test_upload_template(client: TestClient, tmp_path: Path) -> None:
    """POST /upload-template accepts a .docx file and returns stored_name."""
    # Create a minimal .docx file
    from docx import Document

    template_path = tmp_path / "test_template.docx"
    doc = Document()
    doc.add_paragraph("Hello {{name}}!")
    doc.save(str(template_path))

    with open(template_path, "rb") as f:
        resp = client.post(
            "/api/v1/documents/upload-template",
            files={"file": ("test_template.docx", f, "application/vnd.openxmlformats-officedocument.wordprocessingml.document")},
        )

    assert resp.status_code == 200
    data = resp.json()
    assert data["success"] is True
    assert data["stored_name"].startswith("template-")
    assert data["stored_name"].endswith(".docx")


def test_upload_template_wrong_format(client: TestClient) -> None:
    """POST /upload-template with non-.docx returns 400."""
    resp = client.post(
        "/api/v1/documents/upload-template",
        files={"file": ("test.txt", b"not a docx", "text/plain")},
    )
    assert resp.status_code == 400


# ---------------------------------------------------------------------------
# Service unavailable
# ---------------------------------------------------------------------------


def test_service_unavailable(tmp_path: Path) -> None:
    """When document_service is not on app.state, returns 503."""
    app = FastAPI()
    # No document_service set
    app.include_router(documents_routes.router, prefix="/api/v1")
    client = TestClient(app)

    resp = client.post(
        "/api/v1/documents/create",
        json={
            "format": "word",
            "content": "test",
            "conversation_id": "conv-1",
        },
    )
    assert resp.status_code == 503
