"""Shared fixtures for UI integration tests."""

from __future__ import annotations

import asyncio
from collections.abc import AsyncGenerator, Callable
from datetime import UTC, datetime
from pathlib import Path
from typing import Awaitable
from uuid import UUID

import pytest
import pytest_asyncio
from fastapi import FastAPI
from fastapi.testclient import TestClient
from sqlmodel import delete

from transcription.app import create_app
from transcription.config import Settings, SqliteSettings
from transcription.db import create_all, initialize_database_runtime, session_scope
from transcription.db.models import (
    Document,
    DocumentPerson,
    Job,
    JobSource,
    JobSourceStatus,
    JobStatus,
    Person,
    Source,
)


@pytest.fixture(scope="session")
def app_client(tmp_path_factory: pytest.TempPathFactory) -> AsyncGenerator[tuple[FastAPI, TestClient], None]:
    """Provide a real application and test client backed by in-memory SQLite."""
    tmp_path = tmp_path_factory.mktemp("ui")
    settings = Settings(
        openrouter_api_key="test-key",
        database=SqliteSettings(path=":memory:"),
        environment="test",
        bootstrap_schema_on_startup=True,
        upload_dir=tmp_path / "uploads",
        prompt_dir=tmp_path / "prompts",
    )

    app = create_app()
    app.state.runtime = initialize_database_runtime(settings=settings)
    asyncio.run(create_all(engine=app.state.runtime.engine))

    with TestClient(app) as client:
        yield app, client


@pytest_asyncio.fixture(autouse=True)
async def clear_ui_database(app_client: tuple[FastAPI, TestClient]) -> None:
    """Reset UI-facing tables asynchronously before each test for isolation."""
    async with session_scope() as session:
        await session.exec(delete(JobSource))
        await session.exec(delete(DocumentPerson))
        await session.exec(delete(Source))
        await session.exec(delete(Job))
        await session.exec(delete(Document))
        await session.exec(delete(Person))
        await session.commit()


@pytest_asyncio.fixture
async def seed_job(app_client: tuple[FastAPI, TestClient]) -> Callable[..., Awaitable[UUID]]:
    """Return an async helper for seeding a Document -> Job -> Source tuple."""
    app, _ = app_client
    fixtures_dir = Path(__file__).resolve().parents[1] / "fixtures" / "images" / "valid"

    async def _seed(
        *,
        filename: str = "sample.pdf",
        status: JobStatus = JobStatus.TRANSCRIBED,
        transcription_text: str | None = "Sample transcript text",
        error_detail: str | None = None,
        revision_text: str | None = None,
        source_file: Path | None = None,
    ) -> UUID:
        async with session_scope() as session:
            stored_path = app.state.settings.upload_dir / filename
            stored_path.parent.mkdir(parents=True, exist_ok=True)
            source_path = source_file or fixtures_dir / "small_png.png"
            stored_path.write_bytes(source_path.read_bytes())

            document = Document(name=filename)
            session.add(document)
            await session.flush()

            job = Job(
                document_id=document.id,
                status=status,
                retry_count=0,
                provider="openrouter",
                model="google/gemini-2.5-flash",
                prompt_name="transcribe_document.md",
            )
            session.add(job)
            await session.flush()

            source = Source(
                document_id=document.id,
                upload_name=filename,
                filename=filename,
                file_path=str(stored_path),
            )
            session.add(source)
            await session.flush()

            if transcription_text is not None or error_detail is not None:
                session.add(
                    JobSource(
                        job_id=job.id,
                        source_id=source.id,
                        status=(
                            JobSourceStatus.TRANSCRIBED
                            if transcription_text is not None
                            else JobSourceStatus.FAILED
                        ),
                        raw_transcription=transcription_text,
                        error_detail=error_detail,
                    )
                )

            if revision_text is not None:
                source.revised_text = revision_text
                source.date_revised = datetime.now(UTC)
                session.add(source)

            await session.commit()
            return job.id

    return _seed