"""Worker telemetry helpers for operator-facing observability."""

from __future__ import annotations

from typing import Any

from hive_compute.config import get_settings
from hive_compute.coordinator.abuse_events import count_recent_abuse_events, record_abuse_event
from hive_compute.coordinator.database import get_db_ctx
from hive_compute.coordinator.worker_api_serialization import serialize_worker_rows


def record_worker_poll(db, worker_id: str) -> None:
    db.execute(
        """
        UPDATE workers
        SET last_poll_at = datetime('now'),
            capabilities_confirmed_at = datetime('now'),
            status = CASE WHEN status = 'busy' THEN 'busy' ELSE 'idle' END
        WHERE worker_id = ?
        """,
        (worker_id,),
    )


def worker_has_active_claims(db, worker_id: str) -> bool:
    row = db.execute(
        """
        SELECT
            EXISTS(
                SELECT 1
                FROM jobs
                WHERE claimed_by = ?
                  AND status IN ('claimed', 'processing')
            ) AS has_jobs,
            EXISTS(
                SELECT 1
                FROM shards
                WHERE claimed_by = ?
                  AND status IN ('claimed', 'processing')
            ) AS has_shards
        """,
        (worker_id, worker_id),
    ).fetchone()
    return bool(row and (row["has_jobs"] or row["has_shards"]))


def sync_worker_busy_state(
    db,
    worker_id: str,
    *,
    touch_last_poll: bool = False,
    touch_last_heartbeat: bool = False,
) -> None:
    status = "busy" if worker_has_active_claims(db, worker_id) else "idle"
    fields = ["status = ?"]
    params: list[Any] = [status]
    if touch_last_poll:
        fields.append("last_poll_at = datetime('now')")
    if touch_last_heartbeat:
        fields.append("last_heartbeat = datetime('now')")
    params.append(worker_id)
    db.execute(
        f"""
        UPDATE workers
        SET {", ".join(fields)}
        WHERE worker_id = ?
        """,
        params,
    )


def record_worker_claim(db, worker_id: str, job_id: str) -> None:
    db.execute(
        """
        UPDATE workers
        SET status = 'busy',
            last_poll_at = datetime('now'),
            last_heartbeat = datetime('now'),
            last_claimed_at = datetime('now'),
            last_claimed_job_id = ?
        WHERE worker_id = ?
        """,
        (job_id, worker_id),
    )


def record_worker_completion(db, worker_id: str) -> None:
    status = "busy" if worker_has_active_claims(db, worker_id) else "idle"
    db.execute(
        """
        UPDATE workers
        SET status = ?,
            jobs_completed = jobs_completed + 1,
            last_heartbeat = datetime('now')
        WHERE worker_id = ?
        """,
        (status, worker_id),
    )


def record_worker_failure(db, worker_id: str, job_id: str, detail: str) -> None:
    status = "busy" if worker_has_active_claims(db, worker_id) else "idle"
    db.execute(
        """
        UPDATE workers
        SET status = ?,
            last_heartbeat = datetime('now'),
            last_job_failure_at = datetime('now'),
            last_job_failure_detail = ?,
            last_failed_job_id = ?
        WHERE worker_id = ?
        """,
        (status, detail, job_id, worker_id),
    )


def record_worker_auth_failure(worker_id: str, detail: str) -> None:
    with get_db_ctx() as db:
        db.execute(
            """
            UPDATE workers
            SET last_auth_failure_at = datetime('now'),
                last_auth_failure_detail = ?
            WHERE worker_id = ?
            """,
            (detail, worker_id),
        )

    record_abuse_event(
        subject_type="worker",
        subject_id=worker_id,
        event_type="worker_auth_failure",
        severity="low",
        detail=detail,
    )
    recent_failures = count_recent_abuse_events(
        subject_type="worker",
        subject_id=worker_id,
        event_type="worker_auth_failure",
        window_minutes=15,
    )
    if recent_failures >= 3:
        record_abuse_event(
            subject_type="worker",
            subject_id=worker_id,
            event_type="repeated_worker_auth_failures",
            severity="medium",
            detail=f"{recent_failures} invalid worker credential attempts in 15 minutes",
            dedupe_open=True,
        )


def list_workers_with_observability() -> list[dict[str, Any]]:
    stale_minutes = get_settings().stale_job_minutes
    with get_db_ctx() as db:
        rows = db.execute(
            """
            SELECT
                worker_id,
                status,
                chip,
                memory_gb,
                gpu_cores,
                available_handlers,
                available_models,
                model_capabilities,
                capabilities_advertised_at,
                capabilities_confirmed_at,
                last_poll_at,
                last_heartbeat,
                last_claimed_at,
                last_claimed_job_id,
                last_auth_failure_at,
                last_auth_failure_detail,
                last_job_failure_at,
                last_job_failure_detail,
                last_failed_job_id,
                jobs_completed,
                registered_at,
                worker_token_last_four,
                worker_token_issued_at,
                worker_token_revoked_at
            FROM workers
            """
        ).fetchall()
        benchmark_rows = db.execute(
            """
            SELECT wb.*
            FROM worker_benchmarks wb
            WHERE wb.benchmark_id = (
                SELECT wb2.benchmark_id
                FROM worker_benchmarks wb2
                WHERE wb2.worker_id = wb.worker_id
                  AND wb2.model = wb.model
                ORDER BY wb2.benchmark_created_at DESC, wb2.benchmark_id DESC
                LIMIT 1
            )
            ORDER BY wb.worker_id ASC, wb.model ASC
            """
        ).fetchall()

    latest_benchmarks_by_worker: dict[str, list[dict[str, Any]]] = {}
    for benchmark_row in benchmark_rows:
        latest_benchmarks_by_worker.setdefault(benchmark_row["worker_id"], []).append(
            {
                "model": benchmark_row["model"],
                "backend": benchmark_row["backend"],
                "language": benchmark_row["language"],
                "audio_seconds": benchmark_row["audio_seconds"],
                "realtime_factor": benchmark_row["realtime_factor"],
                "memory_rss_mb": benchmark_row["memory_rss_mb"],
                "error": benchmark_row["error"],
                "benchmark_created_at": benchmark_row["benchmark_created_at"],
            }
        )

    workers: list[dict[str, Any]] = []
    for worker in serialize_worker_rows(rows, stale_minutes=stale_minutes):
        payload = worker.model_dump()
        latest_benchmarks = latest_benchmarks_by_worker.get(worker.worker_id, [])
        payload["latest_benchmarks"] = latest_benchmarks
        payload["benchmark_rollup"] = _build_benchmark_rollup(latest_benchmarks)
        workers.append(payload)
    return workers


def _build_benchmark_rollup(latest_benchmarks: list[dict[str, Any]]) -> dict[str, Any] | None:
    if not latest_benchmarks:
        return None

    healthy = [item for item in latest_benchmarks if not item["error"] and item["realtime_factor"] is not None]
    fastest = max(healthy, key=lambda item: item["realtime_factor"]) if healthy else None
    updated_at = max(item["benchmark_created_at"] for item in latest_benchmarks if item["benchmark_created_at"])
    return {
        "updated_at": updated_at,
        "models_tested": len(latest_benchmarks),
        "fastest_model": fastest["model"] if fastest else None,
        "fastest_realtime_factor": fastest["realtime_factor"] if fastest else None,
    }
