from __future__ import annotations

import json
from dataclasses import dataclass
from datetime import datetime
from typing import Any

from database import ReviewDatabase


@dataclass(frozen=True)
class SQLiteJobRunRepository:
    database: ReviewDatabase

    def completed(self, job_id: str, idempotency_key: str) -> bool:
        with self.database.connect() as connection:
            row = connection.execute(
                """
                SELECT 1 FROM job_runs
                WHERE job_id = ? AND idempotency_key = ? AND status = 'success'
                LIMIT 1
                """,
                (job_id, idempotency_key),
            ).fetchone()
        return row is not None

    def start(
        self, job_id: str, idempotency_key: str, output_version: str,
        metadata: dict[str, Any] | None = None,
    ) -> int:
        now = datetime.now().astimezone().isoformat(timespec="seconds")
        with self.database.connect() as connection:
            row = connection.execute(
                """
                SELECT COALESCE(MAX(attempt), 0) + 1 AS attempt FROM job_runs
                WHERE job_id = ? AND idempotency_key = ?
                """,
                (job_id, idempotency_key),
            ).fetchone()
            cursor = connection.execute(
                """
                INSERT INTO job_runs
                    (job_id, idempotency_key, status, attempt, started_at,
                     output_version, metadata)
                VALUES (?, ?, 'running', ?, ?, ?, ?)
                """,
                (
                    job_id, idempotency_key, int(row["attempt"]), now,
                    output_version,
                    json.dumps(metadata or {}, ensure_ascii=False, separators=(",", ":")),
                ),
            )
            connection.execute(
                """
                DELETE FROM job_runs
                WHERE id < (SELECT COALESCE(MAX(id), 0) - 20000 FROM job_runs)
                  AND status != 'running'
                """
            )
            return int(cursor.lastrowid)

    def finish(
        self, run_id: int, status: str, elapsed_ms: int,
        error_code: str = "", message: str = "",
    ) -> None:
        now = datetime.now().astimezone().isoformat(timespec="seconds")
        with self.database.connect() as connection:
            connection.execute(
                """
                UPDATE job_runs
                SET status = ?, finished_at = ?, elapsed_ms = ?,
                    error_code = ?, message = ?
                WHERE id = ?
                """,
                (status, now, elapsed_ms, error_code, message[:1000], int(run_id)),
            )

    def recent(self, limit: int = 20) -> list[dict[str, Any]]:
        with self.database.connect() as connection:
            rows = connection.execute(
                """
                SELECT id, job_id, idempotency_key, status, attempt, started_at,
                       finished_at, elapsed_ms, error_code, message, output_version
                FROM job_runs ORDER BY id DESC LIMIT ?
                """,
                (max(1, min(100, int(limit))),),
            ).fetchall()
        return [dict(row) for row in rows]
