"""Persistence schema metadata and migration helpers."""
from __future__ import annotations

from dataclasses import dataclass, field
from datetime import UTC, datetime
from pathlib import Path
import sqlite3
from typing import Any


SCHEMA_META_TABLE = "logos_adk_schema_meta"


@dataclass
class SchemaStatus:
    """Status of a persisted schema target."""

    kind: str
    backend: str
    location: str
    expected_version: int
    current_version: int = 0
    needs_upgrade: bool = False
    notes: list[str] = field(default_factory=list)


def _utcnow() -> str:
    return datetime.now(UTC).isoformat()


def ensure_sqlite_schema_meta(connection: sqlite3.Connection) -> None:
    """Ensure SQLite schema metadata table exists."""
    connection.execute(
        f"""
        CREATE TABLE IF NOT EXISTS {SCHEMA_META_TABLE} (
            kind TEXT PRIMARY KEY,
            version INTEGER NOT NULL,
            updated_at TEXT NOT NULL
        )
        """
    )


def get_sqlite_schema_version(connection: sqlite3.Connection, kind: str) -> int | None:
    """Fetch the recorded schema version for a SQLite target."""
    ensure_sqlite_schema_meta(connection)
    row = connection.execute(
        f"SELECT version FROM {SCHEMA_META_TABLE} WHERE kind = ?",
        (kind,),
    ).fetchone()
    if row is None:
        return None
    if isinstance(row, sqlite3.Row):
        return int(row["version"])
    return int(row[0])


def set_sqlite_schema_version(connection: sqlite3.Connection, kind: str, version: int) -> None:
    """Record the current schema version for a SQLite target."""
    ensure_sqlite_schema_meta(connection)
    connection.execute(
        f"""
        INSERT INTO {SCHEMA_META_TABLE} (kind, version, updated_at)
        VALUES (?, ?, ?)
        ON CONFLICT(kind) DO UPDATE SET
            version = excluded.version,
            updated_at = excluded.updated_at
        """,
        (kind, version, _utcnow()),
    )


def sqlite_table_exists(connection: sqlite3.Connection, table_name: str) -> bool:
    """Return whether a SQLite table exists."""
    row = connection.execute(
        "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?",
        (table_name,),
    ).fetchone()
    return row is not None


def sqlite_table_columns(connection: sqlite3.Connection, table_name: str) -> set[str]:
    """Return column names for a SQLite table."""
    rows = connection.execute(f"PRAGMA table_info({table_name})").fetchall()
    columns: set[str] = set()
    for row in rows:
        if isinstance(row, sqlite3.Row):
            columns.add(str(row["name"]))
        else:
            columns.add(str(row[1]))
    return columns


def sqlite_schema_status(
    path: str,
    *,
    kind: str,
    expected_version: int,
    notes: list[str] | None = None,
) -> SchemaStatus:
    """Inspect a SQLite schema metadata entry."""
    location = str(Path(path))
    status = SchemaStatus(
        kind=kind,
        backend="sqlite",
        location=location,
        expected_version=expected_version,
        notes=list(notes or []),
    )
    db_path = Path(path)
    if not db_path.exists():
        status.notes.append("database file does not exist yet")
        status.needs_upgrade = True
        return status
    connection = sqlite3.connect(path)
    try:
        connection.row_factory = sqlite3.Row
        status.current_version = get_sqlite_schema_version(connection, kind) or 0
        status.needs_upgrade = status.current_version < expected_version
    finally:
        connection.close()
    return status


def ensure_postgresql_schema_meta(connection: Any) -> None:
    """Ensure PostgreSQL schema metadata table exists."""
    with connection.cursor() as cursor:
        cursor.execute(
            f"""
            CREATE TABLE IF NOT EXISTS {SCHEMA_META_TABLE} (
                kind TEXT PRIMARY KEY,
                version INTEGER NOT NULL,
                updated_at TIMESTAMPTZ NOT NULL
            )
            """
        )


def get_postgresql_schema_version(connection: Any, kind: str) -> int | None:
    """Fetch the recorded schema version for a PostgreSQL target."""
    ensure_postgresql_schema_meta(connection)
    with connection.cursor() as cursor:
        cursor.execute(
            f"SELECT version FROM {SCHEMA_META_TABLE} WHERE kind = %s",
            (kind,),
        )
        row = cursor.fetchone()
    if row is None:
        return None
    try:
        return int(row[0])
    except (TypeError, ValueError, KeyError, IndexError):
        return None


def set_postgresql_schema_version(connection: Any, kind: str, version: int) -> None:
    """Record the current schema version for a PostgreSQL target."""
    ensure_postgresql_schema_meta(connection)
    with connection.cursor() as cursor:
        cursor.execute(
            f"""
            INSERT INTO {SCHEMA_META_TABLE} (kind, version, updated_at)
            VALUES (%s, %s, NOW())
            ON CONFLICT (kind) DO UPDATE SET
                version = EXCLUDED.version,
                updated_at = EXCLUDED.updated_at
            """,
            (kind, version),
        )
