"""Nightly reporting helpers for test_node -> source_file -> symbols test_map."""

from __future__ import annotations

from datetime import date
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from scripts.helpers.ci_gate.models import ExpiredExemptionReport, GatePolicy
    from scripts.helpers.nightly.report_models import MapCoverageSummary

from scripts.helpers.common.ast_utils import MODULE_SYMBOL

UNCLASSIFIED_SYMBOL = MODULE_SYMBOL


def iter_unique_symbol_refs(
    mapping: dict[str, dict[str, list[str]]],
) -> set[tuple[str, str]]:
    """Return unique ``(source_file, symbol)`` pairs covered by *mapping*."""
    refs: set[tuple[str, str]] = set()
    for sources in mapping.values():
        for src_file, symbols in sources.items():
            for symbol in symbols:
                refs.add((src_file, symbol))
    return refs


def summarize_test_map(mapping: dict[str, dict[str, list[str]]]) -> MapCoverageSummary:
    """Build nightly summary counts for the node-oriented test_map."""
    from scripts.helpers.nightly.report_models import MapCoverageSummary

    return MapCoverageSummary(
        test_nodes=len(mapping),
        symbol_refs=len(iter_unique_symbol_refs(mapping)),
    )


def is_source_symbol_mapped(
    mapping: dict[str, dict[str, list[str]]],
    source_file: str,
    symbol: str,
) -> bool:
    """Return True when any test node covers ``source_file::symbol``."""
    for sources in mapping.values():
        for mapped_symbol in sources.get(source_file, ()):
            if mapped_symbol == symbol:
                return True
    return False


def find_expired_unmapped_in_map(
    policy: GatePolicy,
    mapping: dict[str, dict[str, list[str]]],
    *,
    today: date | None = None,
) -> tuple[ExpiredExemptionReport, ...]:
    """Return expired source exemptions still missing from a node-oriented test_map."""
    from scripts.helpers.ci_gate.models import ExpiredExemptionReport

    check_date = today or date.today()
    reports: list[ExpiredExemptionReport] = []
    for entry in policy.source_exemptions:
        if entry.deadline >= check_date:
            continue
        if is_source_symbol_mapped(mapping, entry.file, entry.symbol):
            continue
        reports.append(
            ExpiredExemptionReport(
                symbol_key=entry.symbol_key,
                deadline=entry.deadline,
                reason=entry.reason,
                applicant=entry.applicant,
                approver=entry.approver,
                ticket=entry.ticket,
            )
        )
    return tuple(reports)