"""Naming policy for repository mesh reconciliation."""

from __future__ import annotations

from dataclasses import dataclass
from enum import Enum
from pathlib import Path
from typing import Any, Sequence

from .models import as_plain_data, merge_unique, slugify
from .repository_mesh import RepositoryTarget


class NamingStatus(str, Enum):
    EXACT = "exact"
    ALIAS = "alias"
    CANONICAL_RENAME_REQUIRED = "canonical_rename_required"
    CENTRAL_FOLDER_ONLY = "central_folder_only"
    MISSING = "missing"
    CONFLICT = "conflict"


@dataclass(frozen=True, slots=True)
class NamingCandidate:
    name: str
    path: str
    exists: bool
    is_expected: bool
    is_alias: bool
    is_canonical: bool

    def to_dict(self) -> dict[str, Any]:
        return as_plain_data(self)


@dataclass(frozen=True, slots=True)
class NamingDecision:
    target_name: str
    expected_name: str
    selected_name: str | None
    status: NamingStatus
    reason: str
    candidates: tuple[NamingCandidate, ...]
    next_action: str

    @property
    def needs_action(self) -> bool:
        return self.status not in {NamingStatus.EXACT}

    def to_dict(self) -> dict[str, Any]:
        return as_plain_data(self)


def platform_to_plataform(name: str) -> str:
    if name.endswith("-platform"):
        return name[: -len("-platform")] + "-plataform"
    return name


def plataform_to_platform(name: str) -> str:
    if name.endswith("-plataform"):
        return name[: -len("-plataform")] + "-platform"
    return name


def central_folder_for_name(name: str, index: int | None = None, repo: bool = True) -> str:
    prefix = f"{index:02d}_" if index is not None else ""
    repo_part = "repo_" if repo else ""
    return f"{prefix}{repo_part}{slugify(name)}"


def candidate_names(target: RepositoryTarget) -> tuple[str, ...]:
    names = [target.expected_local_name, target.declared_name]
    if target.canonical_name:
        names.append(target.canonical_name)
    names.extend(target.aliases)
    if target.expected_local_name.endswith("-platform"):
        names.append(platform_to_plataform(target.expected_local_name))
    if target.expected_local_name.endswith("-plataform"):
        names.append(plataform_to_platform(target.expected_local_name))
    return merge_unique(names)


def build_candidates(root: Path, target: RepositoryTarget) -> tuple[NamingCandidate, ...]:
    candidates = []
    for name in candidate_names(target):
        path = root / name
        candidates.append(
            NamingCandidate(
                name=name,
                path=str(path),
                exists=path.exists(),
                is_expected=name == target.expected_local_name,
                is_alias=name in target.aliases,
                is_canonical=bool(target.canonical_name and name == target.canonical_name),
            )
        )
    return tuple(candidates)


def decide_naming(root: Path, target: RepositoryTarget) -> NamingDecision:
    candidates = build_candidates(root, target)
    existing = tuple(item for item in candidates if item.exists)
    expected = next((item for item in candidates if item.is_expected), None)
    if len(existing) > 1:
        return NamingDecision(
            target_name=target.declared_name,
            expected_name=target.expected_local_name,
            selected_name=None,
            status=NamingStatus.CONFLICT,
            reason="mais de uma variante nominal existe; reconciliacao manual evita duplicar repositorio",
            candidates=candidates,
            next_action="comparar HEADs e decidir qual pasta permanece",
        )
    if expected and expected.exists:
        return NamingDecision(
            target_name=target.declared_name,
            expected_name=target.expected_local_name,
            selected_name=expected.name,
            status=NamingStatus.EXACT,
            reason="nome esperado ja esta materializado",
            candidates=candidates,
            next_action="manter monitoramento",
        )
    if len(existing) == 1:
        item = existing[0]
        status = NamingStatus.CANONICAL_RENAME_REQUIRED if target.requires_nominal_reconciliation else NamingStatus.ALIAS
        return NamingDecision(
            target_name=target.declared_name,
            expected_name=target.expected_local_name,
            selected_name=item.name,
            status=status,
            reason=f"pasta encontrada como {item.name}, esperado {target.expected_local_name}",
            candidates=candidates,
            next_action="renomear pasta local com destino inexistente" if status == NamingStatus.CANONICAL_RENAME_REQUIRED else "registrar alias institucional",
        )
    return NamingDecision(
        target_name=target.declared_name,
        expected_name=target.expected_local_name,
        selected_name=None,
        status=NamingStatus.MISSING,
        reason="nenhuma variante nominal foi encontrada",
        candidates=candidates,
        next_action="clonar ou materializar espelho sem sobrescrever destino",
    )


def naming_rows(decisions: Sequence[NamingDecision]) -> list[list[str]]:
    rows = [["target_name", "expected_name", "selected_name", "status", "reason", "next_action"]]
    for decision in decisions:
        rows.append(
            [
                decision.target_name,
                decision.expected_name,
                decision.selected_name or "",
                decision.status.value,
                decision.reason,
                decision.next_action,
            ]
        )
    return rows
