"""Source registry helpers for Phase 2 packets."""

from __future__ import annotations

import json
from pathlib import Path
from typing import Any


def _source_key(source: dict[str, Any]) -> str:
    return (source.get("id") or source.get("source_id") or source.get("doi") or source.get("url") or "").strip()


def append_packet_sources(sources_path: Path, packet: dict[str, Any]) -> int:
    """Append packet sources to sources.jsonl, preserving every citeable source_id."""
    sources_path.parent.mkdir(parents=True, exist_ok=True)
    existing: set[str] = set()
    if sources_path.exists():
        for line in sources_path.read_text(encoding="utf-8").splitlines():
            if not line.strip():
                continue
            try:
                existing.add(_source_key(json.loads(line)))
            except json.JSONDecodeError:
                continue

    written = 0
    with sources_path.open("a", encoding="utf-8") as f:
        for source in packet.get("sources") or []:
            key = _source_key(source)
            if not key or key in existing:
                continue
            existing.add(key)
            f.write(json.dumps(source, ensure_ascii=False) + "\n")
            written += 1
    return written


def rebuild_sources_from_packets(project_root: Path) -> int:
    """Rebuild phase2/sources.jsonl from packet-level source metadata.

    The registry is keyed by source_id, not URL. Two packet sources may point to
    the same URL but have different source_ids already cited in drafts; dropping
    either row would break citation traceability.
    """
    packets_dir = project_root / "phase2" / "packets"
    sources_path = project_root / "phase2" / "sources.jsonl"
    sources_path.parent.mkdir(parents=True, exist_ok=True)
    existing_by_key: dict[str, dict[str, Any]] = {}
    if sources_path.exists():
        for line in sources_path.read_text(encoding="utf-8").splitlines():
            if not line.strip():
                continue
            try:
                row = json.loads(line)
            except json.JSONDecodeError:
                continue
            key = _source_key(row)
            if key:
                existing_by_key[key] = row
    seen: set[str] = set()
    rows: list[dict[str, Any]] = []

    for packet_path in sorted(packets_dir.glob("*.json")):
        try:
            packet = json.loads(packet_path.read_text(encoding="utf-8"))
        except json.JSONDecodeError:
            continue
        for source in packet.get("sources") or []:
            if not isinstance(source, dict):
                continue
            key = _source_key(source)
            if not key or key in seen:
                continue
            seen.add(key)
            previous = existing_by_key.get(key, {})
            rows.append({**source, **{k: v for k, v in previous.items() if k.startswith("cache") or k.startswith("cached_")}})

    sources_path.write_text(
        "".join(json.dumps(row, ensure_ascii=False) + "\n" for row in rows),
        encoding="utf-8",
    )
    return len(rows)
