# ============================================================
# src/accountable_record/stage_010_identity/manifest.py
# ============================================================
"""SE manifest loading for Accountable Record identity checks.

WHY:
    The manifest filename is not fixed by AR-py. The allowed names come from
    se-manifest-schema (see base/constants.allowed_manifest_filenames). This
    module resolves the actual file by trying each allowed name against the
    repo root, in schema order, and reads identity fields from it.
"""

from dataclasses import dataclass
from pathlib import Path
import tomllib

from accountable_record.boundary.toml import is_table
from accountable_record.stage_010_identity.base.constants import (
    allowed_manifest_filenames,
)
from accountable_record.stage_010_identity.base.scalars import (
    optional_string,
    required_string,
)


@dataclass(frozen=True)
class ManifestIdentity:
    """Identity fields read from the SE manifest."""

    filename: str
    repository_name: str
    organization: str
    version: str | None
    status: str | None
    summary: str | None
    raw: dict[str, object]


def find_manifest_path(repo_root: Path) -> Path | None:
    """Return the first allowed manifest file found in repo_root, or None.

    OBS:
        Allowed names are tried in schema order, so the preferred name wins
        when more than one is present.
    """
    for name in allowed_manifest_filenames():
        candidate = repo_root / name
        if candidate.is_file():
            return candidate
    return None


def load_manifest_data(repo_root: Path) -> tuple[str, dict[str, object]]:
    """Load the SE manifest as (filename, table).

    Raises:
        FileNotFoundError: if no allowed manifest filename is present.
        TypeError: if the file does not parse as a TOML table.
    """
    path = find_manifest_path(repo_root)
    if path is None:
        allowed = ", ".join(allowed_manifest_filenames())
        msg = f"Missing SE manifest in {repo_root} (looked for: {allowed})"
        raise FileNotFoundError(msg)

    with path.open("rb") as file:
        value = tomllib.load(file)

    if not is_table(value):
        msg = f"{path.name} did not load as a table"
        raise TypeError(msg)

    return path.name, value


def read_manifest_identity(repo_root: Path) -> ManifestIdentity:
    """Read repository identity fields from the SE manifest.

    Expected shape:

        [repo]
        name = "accountable-record"
        org = "structural-explainability"
        status = "working-draft"
        summary = "..."

        [meta]
        version = "0.1.0"

    The version may also be stored as contract_version under [meta].
    """
    filename, data = load_manifest_data(repo_root)

    repo = data.get("repo")
    if not is_table(repo):
        msg = f"{filename} is missing [repo]"
        raise ValueError(msg)

    meta = data.get("meta")
    if meta is not None and not is_table(meta):
        msg = f"{filename} [meta] must be a table when present"
        raise ValueError(msg)

    repository_name = required_string(repo, "name", f"{filename} [repo]")
    organization = required_string(repo, "org", f"{filename} [repo]")

    version = None
    if is_table(meta):
        version = optional_string(meta, "version")
        if version is None:
            version = optional_string(meta, "contract_version")

    return ManifestIdentity(
        filename=filename,
        repository_name=repository_name,
        organization=organization,
        version=version,
        status=optional_string(repo, "status"),
        summary=optional_string(repo, "summary"),
        raw=data,
    )
