#!/usr/bin/env python3
"""
Validate caption completeness for one day.

A day is caption-complete only when every manifest.images[].file has a
non-empty description in image_descriptions.jsonl and it is not marked as
CAPTION_FAILED.

Exit codes:
  0 = complete
  1 = incomplete
  2 = hard error (missing/invalid inputs)
"""

from __future__ import annotations

import argparse
import json
import sys
from pathlib import Path
from typing import Dict, Iterable, List

ROOT = Path(__file__).resolve().parents[2]
FAILED_PREFIX = "CAPTION_FAILED"


def single_line(text: str) -> str:
    return " ".join((text or "").replace("\r", " ").replace("\n", " ").split()).strip()


def iter_jsonl(path: Path) -> Iterable[dict]:
    if not path.exists():
        return
    with path.open("r", encoding="utf-8") as f:
        for idx, raw_line in enumerate(f, 1):
            line = raw_line.strip()
            if not line:
                continue
            try:
                obj = json.loads(line)
            except json.JSONDecodeError as exc:
                print(f"Warning: skipping invalid jsonl line {idx}: {exc}", file=sys.stderr)
                continue
            if isinstance(obj, dict):
                yield obj
            else:
                print(f"Warning: skipping non-object jsonl line {idx}", file=sys.stderr)


def load_caption_map(path: Path) -> Dict[str, str]:
    """
    Last caption for a file wins (append-only jsonl behavior).
    """
    out: Dict[str, str] = {}
    for obj in iter_jsonl(path):
        file_name = single_line(str(obj.get("file", "")))
        description = single_line(str(obj.get("description", "")))
        if not file_name or not description:
            continue
        out[file_name] = description
    return out


def expected_files_from_manifest(path: Path) -> List[str]:
    try:
        payload = json.loads(path.read_text(encoding="utf-8"))
    except json.JSONDecodeError as exc:
        raise ValueError(f"manifest.json is invalid JSON: {exc}") from exc

    images = payload.get("images", [])
    if not isinstance(images, list):
        raise ValueError("manifest.json has invalid 'images' field (expected list)")

    expected: List[str] = []
    seen = set()
    for item in images:
        if not isinstance(item, dict):
            continue
        file_name = single_line(str(item.get("file", "")))
        if not file_name or file_name in seen:
            continue
        seen.add(file_name)
        expected.append(file_name)
    return expected


def validate_for_date(date_iso: str) -> int:
    day_dir = ROOT / "PHASES" / "PHASE-01-INGEST" / "tweets" / "archive" / date_iso
    manifest_path = day_dir / "manifest.json"
    jsonl_path = day_dir / "image_descriptions.jsonl"

    if not manifest_path.exists():
        print(f"ERROR: missing {manifest_path}")
        return 2

    try:
        expected = expected_files_from_manifest(manifest_path)
    except ValueError as exc:
        print(f"ERROR: {exc}")
        return 2

    caption_map = load_caption_map(jsonl_path)

    failed = 0
    missing: List[str] = []
    for file_name in expected:
        description = caption_map.get(file_name, "").strip()
        if not description:
            missing.append(file_name)
            continue
        if description.startswith(FAILED_PREFIX):
            failed += 1
            missing.append(file_name)

    if missing:
        print(
            "NOT_READY: "
            f"expected={len(expected)} "
            f"captioned={len(expected) - len(missing)} "
            f"missing={len(missing)} "
            f"failed_markers={failed}"
        )
        for file_name in missing[:50]:
            print(f" - {file_name}")
        return 1

    print(
        "OK: all captions present "
        f"(expected={len(expected)}, captioned={len(expected)})"
    )
    return 0


def main() -> int:
    parser = argparse.ArgumentParser(
        description="Validate that all manifest image files have usable captions"
    )
    parser.add_argument("date", help="Date in YYYY-MM-DD")
    args = parser.parse_args()
    return validate_for_date(args.date)


if __name__ == "__main__":
    raise SystemExit(main())
