# ============================================================
# src/accountable_record/pipeline/repository_profile.py
# ============================================================
"""Resolve repository profile from schema-valid manifest facts.

WHY:
    Several stages need to know whether the repository being validated is a
    target Accountable Record repository or an implementation of the verifier.
    That distinction must be resolved once in pipeline context from
    schema-valid manifest facts, not rediscovered or guessed in each stage.

OBS:
    This module is pipeline vocabulary. It must not import stage modules.

OBS:
    Repository profile resolution is deliberately tolerant. Stage 010 owns
    authoritative manifest diagnostics. If the manifest is missing or malformed
    here, the profile falls back to target-material validation so later stages
    do not silently skip required target data.
"""

from dataclasses import dataclass
from pathlib import Path
import tomllib

from accountable_record.boundary.toml import is_table

__all__ = [
    "RepositoryProfile",
    "repository_profile_from_manifest",
    "repository_profile_from_target",
]


MANIFEST_FILENAMES: tuple[str, ...] = (
    "SE_MANIFEST.toml",
    "MANIFEST.toml",
)


@dataclass(frozen=True)
class RepositoryProfile:
    """Resolved repository validation profile."""

    repo_class: str
    layer_role: str
    validates_target_materials: bool

    @property
    def is_implementation(self) -> bool:
        """Return true when the repository is an implementation repo."""
        return self.repo_class == "implementation"


def repository_profile_from_target(repo_root: Path) -> RepositoryProfile:
    """Resolve repository profile from a repository root.

    Args:
        repo_root: Repository root.

    Returns:
        Resolved repository profile. Falls back to target-material validation
        when no readable manifest is available.
    """
    manifest = _load_manifest_or_none(repo_root)
    if manifest is None:
        return _target_materials_profile()

    try:
        return repository_profile_from_manifest(manifest)
    except ValueError:
        return _target_materials_profile()


def repository_profile_from_manifest(manifest: dict[str, object]) -> RepositoryProfile:
    """Resolve repository profile from parsed manifest data."""
    repo = _required_table(manifest, "repo")
    layer = _required_table(manifest, "layer")

    repo_class = _required_string(repo, "class", "[repo]")
    layer_role = _required_string(layer, "role", "[layer]")

    return RepositoryProfile(
        repo_class=repo_class,
        layer_role=layer_role,
        validates_target_materials=_validates_target_materials(
            repo_class=repo_class,
            layer_role=layer_role,
        ),
    )


def _validates_target_materials(*, repo_class: str, layer_role: str) -> bool:
    """Return whether target Accountable Record materials are expected."""
    if repo_class == "implementation":
        return False

    return layer_role != "verifier-implementation"


def _target_materials_profile() -> RepositoryProfile:
    """Return the conservative fallback profile."""
    return RepositoryProfile(
        repo_class="unknown",
        layer_role="unknown",
        validates_target_materials=True,
    )


def _load_manifest_or_none(repo_root: Path) -> dict[str, object] | None:
    """Load the first recognized manifest in repo_root, or None."""
    for filename in MANIFEST_FILENAMES:
        candidate = repo_root / filename
        if not candidate.is_file():
            continue

        try:
            with candidate.open("rb") as handle:
                data = tomllib.load(handle)
        except (OSError, tomllib.TOMLDecodeError):
            return None

        return data

    return None


def _required_table(parent: dict[str, object], key: str) -> dict[str, object]:
    """Return a required table from parsed manifest data."""
    value = parent.get(key)
    if not is_table(value):
        msg = f"manifest requires [{key}] table"
        raise ValueError(msg)
    return value


def _required_string(table: dict[str, object], key: str, context: str) -> str:
    """Return a required non-empty string field."""
    value = table.get(key)
    if not isinstance(value, str) or not value.strip():
        msg = f"{context} requires non-empty string field {key!r}"
        raise ValueError(msg)
    return value.strip()
