"""SQLite persistence for document metadata.

Follows the aiosqlite bare-connection pattern from ``server/auth/models.py``:
no SQLAlchemy session injection, just ``async with aiosqlite.connect(...)``.
The documents table stores metadata; file bytes live on disk under
``data/uploads/``.
"""

from __future__ import annotations

import logging
import os
from collections.abc import Mapping
from pathlib import Path

import aiosqlite

from agentkit.documents.models import DocumentMeta

logger = logging.getLogger(__name__)

_PROJECT_ROOT = Path(__file__).parents[3]
DEFAULT_DOC_DB_PATH = Path(
    os.environ.get("AGENTKIT_DOC_DB", _PROJECT_ROOT / "data" / "documents.db")
)

_SCHEMA_SQL = """
CREATE TABLE IF NOT EXISTS documents (
    id TEXT PRIMARY KEY,
    filename TEXT NOT NULL,
    stored_name TEXT NOT NULL,
    format TEXT NOT NULL,
    size INTEGER NOT NULL,
    conversation_id TEXT NOT NULL,
    created_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_documents_conversation_id
    ON documents(conversation_id);
"""


async def init_documents_db(db_path: str | Path | None = None) -> Path:
    """Create the documents table if it does not exist. Idempotent."""
    path = Path(db_path) if db_path is not None else DEFAULT_DOC_DB_PATH
    path.parent.mkdir(parents=True, exist_ok=True)

    async with aiosqlite.connect(str(path)) as db:
        db.row_factory = aiosqlite.Row
        await db.execute("PRAGMA journal_mode=WAL")
        await db.execute("PRAGMA busy_timeout = 5000")
        await db.executescript(_SCHEMA_SQL)
        await db.commit()

    logger.info(f"Documents DB initialized at {path}")
    return path


def _row_to_meta(row: aiosqlite.Row | Mapping[str, object]) -> DocumentMeta:
    return DocumentMeta(
        id=row["id"],
        filename=row["filename"],
        stored_name=row["stored_name"],
        format=row["format"],
        size=row["size"],
        conversation_id=row["conversation_id"],
        created_at=row["created_at"],
    )


async def insert_document(meta: DocumentMeta, db_path: str | Path | None = None) -> None:
    """Insert a document metadata row."""
    path = Path(db_path) if db_path is not None else DEFAULT_DOC_DB_PATH
    async with aiosqlite.connect(str(path)) as db:
        await db.execute(
            "INSERT INTO documents (id, filename, stored_name, format, size, "
            "conversation_id, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)",
            (
                meta.id,
                meta.filename,
                meta.stored_name,
                meta.format,
                meta.size,
                meta.conversation_id,
                meta.created_at,
            ),
        )
        await db.commit()


async def get_conversation_documents(
    conversation_id: str, db_path: str | Path | None = None
) -> list[DocumentMeta]:
    """Return all documents for a conversation, newest first."""
    path = Path(db_path) if db_path is not None else DEFAULT_DOC_DB_PATH
    async with aiosqlite.connect(str(path)) as db:
        db.row_factory = aiosqlite.Row
        cursor = await db.execute(
            "SELECT * FROM documents WHERE conversation_id = ? ORDER BY created_at DESC",
            (conversation_id,),
        )
        rows = await cursor.fetchall()
        return [_row_to_meta(row) for row in rows]


async def get_document_by_id(doc_id: str, db_path: str | Path | None = None) -> DocumentMeta | None:
    """Return a single document by id, or None if not found."""
    path = Path(db_path) if db_path is not None else DEFAULT_DOC_DB_PATH
    async with aiosqlite.connect(str(path)) as db:
        db.row_factory = aiosqlite.Row
        cursor = await db.execute("SELECT * FROM documents WHERE id = ?", (doc_id,))
        row = await cursor.fetchone()
        return _row_to_meta(row) if row else None


async def delete_document(doc_id: str, db_path: str | Path | None = None) -> bool:
    """Delete a document metadata row. Returns True if a row was deleted."""
    path = Path(db_path) if db_path is not None else DEFAULT_DOC_DB_PATH
    async with aiosqlite.connect(str(path)) as db:
        cursor = await db.execute("DELETE FROM documents WHERE id = ?", (doc_id,))
        await db.commit()
        return cursor.rowcount > 0
