"""Repo-owned git policy config loader (`.aida/git_policy.yaml`)."""

from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path
from typing import cast

import yaml

from aida_mcp.utils.aida_paths import get_repo_config_root


@dataclass(frozen=True, slots=True)
class CommitPolicy:
    subject_max_chars: int = 70
    risk_values: tuple[str, ...] = ("nonprod", "low", "high")
    require_ai_share: bool = False
    require_co_authored_by: bool = False
    enable_ai_share_auto: bool = True


@dataclass(frozen=True, slots=True)
class WorkflowPolicy:
    prefer_force_with_lease: bool = True
    autosquash_unpublished_only: bool = True


@dataclass(frozen=True, slots=True)
class GitPolicy:
    commit: CommitPolicy = CommitPolicy()
    workflow: WorkflowPolicy = WorkflowPolicy()


def load_git_policy(workspace_root: Path) -> GitPolicy:
    """Load `.aida/git_policy.yaml`; return defaults on missing/invalid config."""
    path = get_repo_config_root(workspace_root) / "git_policy.yaml"
    if not path.exists():
        return GitPolicy()

    try:
        loaded: object = yaml.safe_load(path.read_text(encoding="utf-8"))
    except Exception:
        return GitPolicy()
    if not isinstance(loaded, dict):
        return GitPolicy()
    root = cast(dict[str, object], loaded)

    section = _mapping(root.get("git_policy"), fallback={})
    commit = _parse_commit_policy(section.get("commit"))
    workflow = _parse_workflow_policy(section.get("workflow"))
    return GitPolicy(commit=commit, workflow=workflow)


def _parse_commit_policy(raw: object) -> CommitPolicy:
    section = _mapping(raw, fallback={})
    subject_max = _int_in_range(section.get("subject_max_chars"), fallback=70, low=10, high=200)
    risks = _risk_values(section.get("risk_values"))
    require_ai_share = _bool(section.get("require_ai_share"), fallback=False)
    require_co_authored_by = _bool(section.get("require_co_authored_by"), fallback=False)
    enable_ai_share_auto = _bool(section.get("enable_ai_share_auto"), fallback=True)
    return CommitPolicy(
        subject_max_chars=subject_max,
        risk_values=risks,
        require_ai_share=require_ai_share,
        require_co_authored_by=require_co_authored_by,
        enable_ai_share_auto=enable_ai_share_auto,
    )


def _parse_workflow_policy(raw: object) -> WorkflowPolicy:
    section = _mapping(raw, fallback={})
    prefer_lease = _bool(section.get("prefer_force_with_lease"), fallback=True)
    autosquash = _bool(section.get("autosquash_unpublished_only"), fallback=True)
    return WorkflowPolicy(prefer_force_with_lease=prefer_lease, autosquash_unpublished_only=autosquash)


def _risk_values(raw: object) -> tuple[str, ...]:
    if not isinstance(raw, list):
        return ("nonprod", "low", "high")
    values: list[str] = []
    for item in cast(list[object], raw):
        if isinstance(item, str):
            value = item.strip().lower()
            if value and value not in values:
                values.append(value)
    if not values:
        return ("nonprod", "low", "high")
    return tuple(values)


def _mapping(raw: object, *, fallback: dict[str, object]) -> dict[str, object]:
    if isinstance(raw, dict):
        return cast(dict[str, object], raw)
    return fallback


def _bool(raw: object, *, fallback: bool) -> bool:
    if isinstance(raw, bool):
        return raw
    return fallback


def _int_in_range(raw: object, *, fallback: int, low: int, high: int) -> int:
    if isinstance(raw, bool):
        return fallback
    if isinstance(raw, int) and low <= raw <= high:
        return raw
    return fallback
