"""Enrichment background jobs — async link/file processing."""

from __future__ import annotations

import json
import threading
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Callable
from uuid import uuid4


@dataclass
class EnrichmentJobRecord:
    job_id: str
    status: str
    session_id: str
    turn_id: str
    enrichment_key: str
    kind: str
    enrichment: dict[str, Any] | None = None
    error: str | None = None

    def to_dict(self) -> dict[str, Any]:
        out: dict[str, Any] = {
            "job_id": self.job_id,
            "status": self.status,
            "session_id": self.session_id,
            "turn_id": self.turn_id,
            "enrichment_key": self.enrichment_key,
            "kind": self.kind,
        }
        if self.enrichment is not None:
            out["enrichment"] = self.enrichment
        if self.error:
            out["error"] = self.error
        return out


class EnrichmentJobStore:
    def __init__(self, home: Path | None) -> None:
        self._home = home
        self._lock = threading.Lock()
        self._jobs: dict[str, EnrichmentJobRecord] = {}
        self._path = (home / "data" / "state" / "enrichment_jobs.json") if home else None
        self._load()

    def _load(self) -> None:
        if self._path is None or not self._path.is_file():
            return
        try:
            raw = json.loads(self._path.read_text(encoding="utf-8"))
        except (OSError, json.JSONDecodeError):
            return
        if not isinstance(raw, dict):
            return
        for job_id, row in raw.items():
            if isinstance(row, dict):
                self._jobs[job_id] = EnrichmentJobRecord(
                    job_id=str(job_id),
                    status=str(row.get("status") or "pending"),
                    session_id=str(row.get("session_id") or ""),
                    turn_id=str(row.get("turn_id") or ""),
                    enrichment_key=str(row.get("enrichment_key") or ""),
                    kind=str(row.get("kind") or ""),
                    enrichment=row.get("enrichment") if isinstance(row.get("enrichment"), dict) else None,
                    error=str(row["error"]) if row.get("error") else None,
                )

    def _persist(self) -> None:
        if self._path is None:
            return
        self._path.parent.mkdir(parents=True, exist_ok=True)
        payload = {jid: rec.to_dict() for jid, rec in self._jobs.items()}
        self._path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")

    def get(self, job_id: str) -> EnrichmentJobRecord | None:
        with self._lock:
            return self._jobs.get(job_id)

    def enqueue(
        self,
        *,
        session_id: str,
        turn_id: str,
        enrichment_key: str,
        kind: str,
        worker: Callable[[], dict[str, Any] | None],
        on_complete: Callable[[str, dict[str, Any] | None, str | None], None],
    ) -> str:
        job_id = uuid4().hex[:12]
        record = EnrichmentJobRecord(
            job_id=job_id,
            status="pending",
            session_id=session_id,
            turn_id=turn_id,
            enrichment_key=enrichment_key,
            kind=kind,
        )
        with self._lock:
            self._jobs[job_id] = record
            self._persist()

        def _run() -> None:
            err: str | None = None
            enrichment: dict[str, Any] | None = None
            try:
                enrichment = worker()
                status = "done" if enrichment is not None else "failed"
                if enrichment is None:
                    err = "empty_result"
            except Exception as exc:
                status = "failed"
                err = type(exc).__name__
            with self._lock:
                rec = self._jobs.get(job_id)
                if rec is not None:
                    rec.status = status
                    rec.enrichment = enrichment
                    rec.error = err
                    self._persist()
            try:
                on_complete(job_id, enrichment, err)
            except Exception:
                pass

        threading.Thread(target=_run, daemon=True).start()
        return job_id
