from typing import TYPE_CHECKING

from recoil.core.ref_stem import subject_id_norm
from recoil.core.ref_errors import MissingRequiredRefError, MissingBoardRefError

if TYPE_CHECKING:
    from recoil.core.ref_types import ReferenceBundle


def assert_refs_complete(
    *, shot_id: str,
    required_subjects: list[str],
    bundle: "ReferenceBundle",
    board_gated: bool,
    board_ref_path: str | None,
    refless_declared: bool = False,
) -> None:
    """Fail-closed contract, called AFTER resolution, BEFORE dispatch.

    - refless_declared: empty bundle is OK.
    - Else every required_subject (normalized via subject_id_norm) must appear in
      bundle.hero_subjects() (a USABLE hero, not merely an identity-role asset).
    - board_gated and not board_ref_path -> MissingBoardRefError.
    """
    if not refless_declared:
        heroes = bundle.hero_subjects()
        missing = [
            s for s in required_subjects
            if subject_id_norm(s) not in heroes
        ]
        if missing:
            raise MissingRequiredRefError(shot_id, missing)
    if board_gated and not board_ref_path:
        raise MissingBoardRefError(shot_id)
