# ============================================================
# src/accountable_record/stage_090_docs/plan.py
# ============================================================
"""Plan stage 090 documentation pages from the generated artifacts.

WHY:
    Stage 090 documents the generated element artifacts from stage 080. plan.py
    derives one planned page per generated element and reconciles the planned
    page set against the generated set. It is the op the validation layer
    drives. Like earlier read ops it stays free of diagnostics: it returns a
    plan and a reconciliation, raising nothing for ordinary parity gaps (those
    become diagnostics in the validation layer).

OBS (plan, not write):
    Planning derives paths and titles only. It reads no files and writes
    nothing. render.py realizes the plan into in-memory content.

OBS (data-driven):
    Pages are derived from the generated element set, not from re-walking
    docs/. No hardcoded page names or counts.
"""

from collections import Counter

from accountable_record.stage_080_generated.model import GeneratedRegistry
from accountable_record.stage_090_docs.base.scalars import (
    page_rel_path_for,
    page_stem_for,
)
from accountable_record.stage_090_docs.model import (
    DocsReconciliation,
    DocsRenderPlan,
    PlannedPage,
)

__all__ = ["plan_documentation", "reconcile_plan_with_generated"]


def plan_documentation(registry: GeneratedRegistry) -> DocsRenderPlan:
    """Derive a documentation render plan from the generated registry.

    Args:
        registry: The stage 080 generated element registry.

    Returns:
        A render plan with one planned page per generated element, in sorted
        page-path order.
    """
    pages = [
        PlannedPage(
            compact_id=record.compact_id,
            rel_path=page_rel_path_for(record.compact_id),
            title=_title_for(record.compact_id),
        )
        for record in registry.records
    ]
    pages.sort(key=lambda page: page.rel_path)
    return DocsRenderPlan(pages=tuple(pages))


def reconcile_plan_with_generated(
    plan: DocsRenderPlan,
    registry: GeneratedRegistry,
) -> DocsReconciliation:
    """Reconcile the page plan against the generated element set.

    Args:
        plan: The documentation render plan.
        registry: The stage 080 generated element registry.

    Returns:
        A reconciliation naming generated elements with no planned page,
        planned pages with no generated element, and page paths that two
        planned pages share.
    """
    planned_ids = plan.compact_ids()
    generated_ids = registry.compact_ids()

    return DocsReconciliation(
        generated_missing_page=tuple(sorted(generated_ids - planned_ids)),
        page_missing_generated=tuple(sorted(planned_ids - generated_ids)),
        duplicate_page_paths=_duplicate_paths(plan.rel_paths()),
    )


def _title_for(compact_id: str) -> str:
    """Return a human page title derived from the element local name."""
    stem = page_stem_for(compact_id)
    return stem.replace("-", " ").replace("_", " ").title()


def _duplicate_paths(rel_paths: tuple[str, ...]) -> tuple[str, ...]:
    """Return sorted page paths that appear more than once."""
    counter = Counter(rel_paths)
    return tuple(sorted(path for path, count in counter.items() if count > 1))
