"""Brand manifest loader — parse brand-manifest.yaml into a typed dataclass.

See ``examples/brand-repos/winsemius-brand/brand-manifest.yaml`` for the
canonical example.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from pathlib import Path
from typing import Any

import yaml

from tokenmoulds.pipeline import BuildConfig


@dataclass
class BrandManifest:
    """Typed representation of a ``brand-manifest.yaml`` file."""

    # Identity
    schema_version: int
    brand_id: str
    display_name: str
    source_url: str
    default_locale: str
    artifact_prefix: str

    # Token source (relative path to .tokens.json)
    token_source: str

    # Build
    formats: list[str] = field(default_factory=list)
    output_extensions: dict[str, str] = field(default_factory=dict)

    # Policy
    policy_profile: str | None = None

    # Release
    release_strategy: str = "tag"
    preview_on_pr: bool = True

    # Bootstrap (optional)
    bootstrap: dict[str, str] | None = None


def load_manifest(path: Path) -> BrandManifest:
    """Parse a ``brand-manifest.yaml`` file into a :class:`BrandManifest`.

    Args:
        path: Path to the YAML manifest file.

    Returns:
        Parsed manifest dataclass.

    Raises:
        FileNotFoundError: If the manifest file does not exist.
        ValueError: If required fields are missing.
    """
    if not path.exists():
        raise FileNotFoundError(f"Manifest not found: {path}")

    with open(path, encoding="utf-8") as fh:
        data: dict[str, Any] = yaml.safe_load(fh)

    if data is None:
        raise ValueError(f"Empty manifest: {path}")

    # Extract nested structures
    build = data.get("build", {})
    policy = data.get("policy", {})
    release = data.get("release", {})

    return BrandManifest(
        schema_version=data["schema_version"],
        brand_id=data["brand_id"],
        display_name=data["display_name"],
        source_url=data.get("source_url", ""),
        default_locale=data.get("default_locale", "US"),
        artifact_prefix=data.get("artifact_prefix", data["brand_id"]),
        token_source=data["token_source"],
        formats=build.get("formats", []),
        output_extensions=build.get("output_extensions", {}),
        policy_profile=policy.get("validation_profile"),
        release_strategy=release.get("strategy", "tag"),
        preview_on_pr=release.get("preview_on_pull_request", True),
        bootstrap=data.get("bootstrap"),
    )


def manifest_to_build_config(
    manifest: BrandManifest,
    manifest_dir: Path,
) -> BuildConfig:
    """Convert a :class:`BrandManifest` into a :class:`BuildConfig`.

    Args:
        manifest: Parsed brand manifest.
        manifest_dir: Directory containing the manifest (used to resolve
            relative paths such as ``token_source``).

    Returns:
        A fully populated :class:`BuildConfig`.
    """
    # Map format names through output_extensions to get file extensions
    output_formats = [
        manifest.output_extensions.get(fmt, fmt) for fmt in manifest.formats
    ]

    return BuildConfig(
        org_id=manifest.brand_id,
        output_formats=output_formats,
        tokens_file=manifest_dir / manifest.token_source,
        locale=manifest.default_locale,
        validate=True,
    )
