from __future__ import annotations

import csv
import json
from pathlib import Path

from src.errors import UserError


CSV_COLUMNS = [
    "photo_sha256",
    "source_path",
    "source_relpath",
    "deal_id",
    "confidence",
    "match_reason",
    "website_webp_path",
    "website_webp_exists",
    "website_webp_bytes",
    "gbp_jpg_path",
    "gbp_jpg_exists",
    "gbp_jpg_bytes",
    "gbp_png_path",
    "gbp_png_exists",
    "gbp_png_bytes",
]


def export_audit(*, out_dir: Path, run_id: str | None, dry_run: bool) -> list[dict[str, object]]:
    run_dir = _resolve_run_dir(out_dir=out_dir, run_id=run_id)
    audit_jsonl = run_dir / "audit.jsonl"
    if not audit_jsonl.exists():
        raise UserError(f"Audit file not found: {audit_jsonl}")

    records = _read_jsonl(audit_jsonl)
    if not dry_run:
        _write_json_atomic(run_dir / "audit.json", records)
        _write_csv_atomic(run_dir / "audit.csv", records)
    return records


def _resolve_run_dir(*, out_dir: Path, run_id: str | None) -> Path:
    root = out_dir / "audit"
    if run_id:
        run_dir = root / run_id
        if not run_dir.exists():
            raise UserError(f"Run not found: {run_dir}")
        return run_dir

    if not root.exists():
        raise UserError(f"Audit root not found: {root}")
    dirs = sorted([p for p in root.iterdir() if p.is_dir()])
    if not dirs:
        raise UserError(f"No runs found under: {root}")
    return dirs[-1]


def _read_jsonl(path: Path) -> list[dict[str, object]]:
    out: list[dict[str, object]] = []
    for i, line in enumerate(path.read_text(encoding="utf-8").splitlines(), start=1):
        if not line.strip():
            continue
        try:
            obj = json.loads(line)
        except json.JSONDecodeError as exc:
            raise UserError(f"Invalid JSONL at {path}:{i}: {exc.msg}") from exc
        if not isinstance(obj, dict):
            raise UserError(f"Invalid JSONL record at {path}:{i}: expected object")
        out.append(obj)
    return out


def _write_csv_atomic(path: Path, records: list[dict[str, object]]) -> None:
    tmp = path.with_suffix(path.suffix + ".tmp")
    try:
        with tmp.open("w", encoding="utf-8", newline="") as f:
            w = csv.DictWriter(f, fieldnames=CSV_COLUMNS, extrasaction="ignore")
            w.writeheader()
            for r in records:
                w.writerow({k: r.get(k) for k in CSV_COLUMNS})
        tmp.replace(path)
    except OSError as exc:
        raise UserError(f"Failed to write CSV: {path} ({exc})") from exc


def _write_json_atomic(path: Path, records: list[dict[str, object]]) -> None:
    text = json.dumps(records, indent=2, sort_keys=True, ensure_ascii=True) + "\n"
    tmp = path.with_suffix(path.suffix + ".tmp")
    try:
        tmp.write_text(text, encoding="utf-8", newline="\n")
        tmp.replace(path)
    except OSError as exc:
        raise UserError(f"Failed to write JSON: {path} ({exc})") from exc
