"""Review queue -- append-only JSONL with fcntl.flock for concurrent write safety.

The review queue stores shots that need human triage (attempts exhausted,
unfixable failures, ICU escalations). JT reviews these in the Production
Console Dailies tab each morning.

Design:
- Append-only JSONL: enqueue() and resolve() both APPEND new lines.
- resolve() does NOT rewrite the file -- it appends a new line with
  {rq_id, status: "resolved", ...}. list_pending() keeps latest state per
  rq_id by overwriting in the dict during scan.
- fcntl.flock(LOCK_EX) for concurrent write safety (same pattern as Phase 2.5
  experience pool).
- uuid.uuid7() for rq_id generation (Python 3.14+, spec D10).
- 7-day stale threshold: flagged in list_pending(), never auto-rejected.
"""

from __future__ import annotations

import json
import sys
import uuid
from datetime import datetime, timezone, timedelta
from pathlib import Path
from typing import Any

from recoil.pipeline._lib.jsonl_append import append_jsonl_locked


def _now_iso() -> str:
    return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")


def _make_rq_id() -> str:
    """Generate review queue id: 'rq_' + full uuid7 hex.

    Uses full 32-char hex (not truncated) to guarantee uniqueness even
    for rapid-fire calls within the same millisecond. uuid7's first 12
    hex chars are timestamp-only and collide at sub-ms intervals.
    """
    if sys.version_info < (3, 14):
        raise RuntimeError("uuid.uuid7() requires Python 3.14+. See spec D10.")
    return "rq_" + uuid.uuid7().hex


def _append_line(queue_path: Path, record: dict[str, Any]) -> None:
    """Append a single JSON line to the queue file under flock."""
    line = json.dumps(record, separators=(",", ":")) + "\n"
    append_jsonl_locked(queue_path, line)


def enqueue(
    *,
    queue_path: Path,
    project: str,
    episode_id: str,
    shot_id: str,
    run_id: str,
    reason: str,
    failure_mode: str,
    attempts: list[dict],
    total_cost_usd: float,
    original_prompt: str = "",
    extra: dict | None = None,
) -> dict:
    """Append a new pending review entry. Returns the entry dict (with rq_id)."""
    entry = {
        "rq_id": _make_rq_id(),
        "status": "pending",
        "created_at": _now_iso(),
        "project": project,
        "episode_id": episode_id,
        "shot_id": shot_id,
        "run_id": run_id,
        "reason": reason,
        "failure_mode": failure_mode,
        "attempts": attempts,
        "total_cost_usd": total_cost_usd,
        "original_prompt": original_prompt,
    }
    if extra:
        entry["extra"] = extra
    _append_line(queue_path, entry)
    return entry


def list_pending(
    *,
    queue_path: Path,
    flag_stale_days: int = 7,
) -> list[dict]:
    """Return all entries whose latest state is 'pending'.

    Scans the full file, keeping latest state per rq_id (later lines override
    earlier ones). Entries older than flag_stale_days are annotated with
    stale=True but never auto-rejected.
    """
    if not queue_path.exists():
        return []

    entries: dict[str, dict] = {}
    with queue_path.open() as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            try:
                record = json.loads(line)
                rq_id = record.get("rq_id")
                if rq_id:
                    entries[rq_id] = record
            except (json.JSONDecodeError, KeyError, TypeError):
                continue

    now = datetime.now(timezone.utc)
    stale_cutoff = now - timedelta(days=flag_stale_days)

    pending = []
    for entry in entries.values():
        if entry.get("status") != "pending":
            continue
        # Flag stale entries
        created_str = entry.get("created_at", "")
        if created_str:
            try:
                created = datetime.fromisoformat(created_str)
                if created < stale_cutoff:
                    entry["stale"] = True
            except (ValueError, TypeError):
                pass
        pending.append(entry)

    return pending


def list_all(
    *,
    queue_path: Path,
) -> list[dict]:
    """Return all entries with their latest state (resolved entries show resolved status).

    Scans the full file, keeping latest state per rq_id.
    """
    if not queue_path.exists():
        return []

    entries: dict[str, dict] = {}
    with queue_path.open() as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            try:
                record = json.loads(line)
                rq_id = record.get("rq_id")
                if rq_id:
                    entries[rq_id] = record
            except (json.JSONDecodeError, KeyError, TypeError):
                continue

    return list(entries.values())


def resolve(
    *,
    queue_path: Path,
    rq_id: str,
    resolution: str,
    notes: str = "",
) -> dict:
    """Resolve a review queue entry by APPENDING a new line with resolved status.

    Does NOT rewrite the file -- preserves the append-only contract.
    list_pending() and list_all() keep latest state per rq_id during scan.
    """
    record = {
        "rq_id": rq_id,
        "status": "resolved",
        "resolution": resolution,
        "resolved_at": _now_iso(),
        "notes": notes,
    }
    _append_line(queue_path, record)
    return record
