# -*- coding: utf-8 -*-
"""anu_v3.consolidated_summary_candidate_generator — last-settle collector
3-track consolidated summary *candidate* generator (task-2553+53 §3.5).

§3.5 verbatim: "3 track 전부 AUTHORITATIVE_PASS 면 마지막 settle collector 가
consolidated summary candidate 생성." 이 모듈은 ``anu_v3.batch_settle_
writeback.evaluate_batch_settle`` 의 ``ALL_SETTLED_CONSOLIDATE`` 결정을
입력받아 **candidate**(최종 아님) 를 생성한다. all-settled 가 아니거나
한 track 이라도 AUTHORITATIVE_PASS 가 아니면 candidate 를 만들지 않는다
(fail-closed — 과장된 통합 산출 0).

이 산출물은 *candidate* 이다: 회장/last-settle 독립 ANU 가 최종 확정한다.
기존 +50/+51/+52 산출물은 byte-0 (read-only consume) — 본 모듈은 그
verdict 파일을 *읽기* 만 하고 통합 후보를 *추가* 산출만 한다 (§3.10).

Layer A / NO-CRON: 순수 생성. ZERO cron / dispatch / subprocess / cokacdir
/ merge / PR. write 는 호출자(allowlist gated result JSON/MD)가 수행.
"""
from __future__ import annotations

import json
from pathlib import Path
from typing import Dict, List, Optional

from anu_v3.batch_settle_writeback import (
    AUTHORITATIVE_PASS,
    BatchSettleResult,
)

CANDIDATE_SCHEMA = "task-2553.batch-consolidated-summary-candidate.v1"


def _read_json(path: str) -> Dict[str, object]:
    try:
        return json.loads(Path(path).read_text(encoding="utf-8"))
    except (OSError, json.JSONDecodeError):
        return {}


def generate_consolidated_summary_candidate(
    settle: BatchSettleResult,
    *,
    source_paths_by_track: Dict[str, str],
    canonical_root: str = "/home/jay/workspace",
) -> Optional[Dict[str, object]]:
    """Return the consolidated summary *candidate* dict, or ``None`` when
    the batch is NOT all-settled / not all-AUTHORITATIVE_PASS (§3.5,
    fail-closed — never a premature/overstated consolidation).

    ``source_paths_by_track`` maps track_id -> the independent-ANU verdict
    file (read-only) backing that track's durable-success.
    """
    if settle.decision != "ALL_SETTLED_CONSOLIDATE":
        return None
    if not (settle.all_settled and settle.all_authoritative_pass):
        return None

    per_track: List[Dict[str, object]] = []
    for ts in settle.track_states:
        trk = str(ts.get("track_id"))
        verdict = ts.get("authoritative_verdict")
        if verdict != AUTHORITATIVE_PASS:
            # fail-closed: a non-AUTHORITATIVE_PASS slipped through -> no
            # candidate (defensive; evaluate_batch_settle already gates).
            return None
        src = source_paths_by_track.get(trk, "")
        src_data = _read_json(
            str(Path(canonical_root) / src)
        ) if src and not src.startswith("/") else _read_json(src)
        per_track.append(
            {
                "track_id": trk,
                "task_id": ts.get("task_id"),
                "authoritative_verdict": verdict,
                "terminal_status": ts.get("terminal_status"),
                "source_result_path": src,
                "collector_role": src_data.get("collector_role", "ANU"),
                "hold_for_chair": bool(src_data.get("hold_for_chair")),
            }
        )

    any_hold = any(t["hold_for_chair"] for t in per_track)

    return {
        "schema": CANDIDATE_SCHEMA,
        "kind": "CANDIDATE",
        "status": "CONSOLIDATED_SUMMARY_CANDIDATE_READY",
        "batch_id": settle.batch_id,
        "generated_basis": settle.evaluated_at_basis,
        "all_settled": settle.all_settled,
        "all_authoritative_pass": settle.all_authoritative_pass,
        "tracks": per_track,
        "track_count": len(per_track),
        "verdict_summary": (
            f"{len(per_track)}/{len(per_track)} tracks AUTHORITATIVE_PASS "
            "(independent ANU collector, self-chain excluded via +49 정본)"
        ),
        "hold_for_chair": any_hold,
        "chair_report_required": any_hold,
        "consolidation_trigger": (
            "normal-callback durable-success event (3/3 durable_success_"
            "writeback.v1 COMPLETED in +44 ledger) — NOT fixed-time / "
            "dead-man / fallback (§3.5/§3.7)."
        ),
        "note": (
            "CANDIDATE only — final consolidation is confirmed by the "
            "last-settle independent ANU collector. 기존 +50/+51/+52 "
            "산출물 byte-0 (read-only consume); additive only (§3.10)."
        ),
    }


def render_candidate_markdown(candidate: Dict[str, object]) -> str:
    """Human-readable MD rendering of the consolidated summary candidate."""
    lines: List[str] = []
    lines.append(
        "# task-2553 3-track batch — consolidated summary CANDIDATE"
    )
    lines.append("")
    lines.append(f"> batch_id: `{candidate.get('batch_id')}`")
    lines.append(
        f"> basis: {candidate.get('generated_basis')} · "
        f"status: **{candidate.get('status')}**"
    )
    lines.append(
        f"> {candidate.get('verdict_summary')} · "
        f"hold_for_chair: **{candidate.get('hold_for_chair')}**"
    )
    lines.append("")
    lines.append("## Tracks")
    lines.append("")
    for t in candidate.get("tracks", []):  # type: ignore[arg-type]
        lines.append(
            f"- **{t.get('track_id')}** `{t.get('task_id')}` — "
            f"{t.get('authoritative_verdict')} "
            f"({t.get('terminal_status')}) · "
            f"src `{t.get('source_result_path')}`"
        )
    lines.append("")
    lines.append("## Trigger")
    lines.append("")
    lines.append(str(candidate.get("consolidation_trigger")))
    lines.append("")
    lines.append("## Note")
    lines.append("")
    lines.append(str(candidate.get("note")))
    lines.append("")
    return "\n".join(lines)


__all__ = [
    "CANDIDATE_SCHEMA",
    "generate_consolidated_summary_candidate",
    "render_candidate_markdown",
]
