"""Template repo config manager to centralize repo-config mutations."""

from pathlib import Path
from typing import Any, Optional

import typer
import yaml

from ...core.config import (
    CONFIG_FILENAME,
    GENERATED_CONFIG_HEADER,
    load_hub_config,
    save_hub_config_data,
)
from ...core.locks import file_lock


class TemplatesConfigError(Exception):
    """Error in templates configuration."""


class TemplateReposManager:
    """Manager for template repos in hub config."""

    def __init__(self, hub_config_path: Path) -> None:
        """Initialize the manager with a hub config path."""
        self.hub_config_path = hub_config_path
        self._data: dict[str, Any] = {}
        self._generated = False
        self._templates_enabled = True
        self._repos: list[dict[str, Any]] = []
        self._load()

    def _load(self) -> None:
        """Load the hub config YAML."""
        if not self.hub_config_path.exists():
            raise TemplatesConfigError(
                f"Hub config file not found: {self.hub_config_path}"
            )
        try:
            raw_text = self.hub_config_path.read_text(encoding="utf-8")
            self._generated = raw_text.startswith(GENERATED_CONFIG_HEADER)
            data = yaml.safe_load(raw_text)
            if not isinstance(data, dict):
                raise TemplatesConfigError(
                    f"Hub config must be a YAML mapping: {self.hub_config_path}"
                )
            resolved = load_hub_config(self.hub_config_path.parent.parent.resolve())
            self._data = data
            self._templates_enabled = resolved.templates.enabled
            self._repos = [
                {
                    "id": repo.id,
                    "url": repo.url,
                    "trusted": bool(repo.trusted),
                    "default_ref": repo.default_ref,
                }
                for repo in resolved.templates.repos
            ]
        except yaml.YAMLError as exc:
            raise TemplatesConfigError(f"Invalid YAML in hub config: {exc}") from exc
        except OSError as exc:
            raise TemplatesConfigError(f"Failed to read hub config: {exc}") from exc

    def save(self) -> None:
        """Save the hub config YAML."""
        lock_path = self.hub_config_path.parent / (self.hub_config_path.name + ".lock")
        with file_lock(lock_path):
            save_hub_config_data(
                self.hub_config_path,
                self._data,
                generated=self._generated,
            )

    def list_repos(self) -> list[dict[str, Any]]:
        """List all configured template repos."""
        return [dict(repo) for repo in self._repos]

    def _sync_templates_config(self) -> None:
        templates_config = self._data.setdefault("templates", {})
        if not isinstance(templates_config, dict):
            raise TemplatesConfigError("Invalid templates config in hub config")
        if not self._templates_enabled or "enabled" in templates_config:
            templates_config["enabled"] = self._templates_enabled
        templates_config["repos"] = [dict(repo) for repo in self._repos]

    def add_repo(
        self,
        repo_id: str,
        url: str,
        trusted: Optional[bool] = None,
        default_ref: str = "main",
    ) -> None:
        """Add a template repo."""
        self._require_templates_enabled()

        existing_ids = {
            repo.get("id") for repo in self._repos if isinstance(repo, dict)
        }
        if repo_id in existing_ids:
            raise TemplatesConfigError(
                f"Repo ID '{repo_id}' already exists. Use a unique ID."
            )

        from typing import Union

        new_repo: dict[str, Union[str, bool]] = {
            "id": repo_id,
            "url": url,
            "default_ref": default_ref,
        }
        if trusted is not None:
            new_repo["trusted"] = trusted

        self._repos.append(new_repo)
        self._sync_templates_config()

    def remove_repo(self, repo_id: str) -> None:
        """Remove a template repo."""
        original_count = len(self._repos)
        filtered_repos = [
            repo
            for repo in self._repos
            if isinstance(repo, dict) and repo.get("id") != repo_id
        ]

        if len(filtered_repos) == original_count:
            raise TemplatesConfigError(f"Repo ID '{repo_id}' not found in config.")

        self._repos = filtered_repos
        self._sync_templates_config()

    def set_trusted(self, repo_id: str, trusted: bool) -> None:
        """Set the trusted status of a template repo."""
        found = False
        for repo in self._repos:
            if isinstance(repo, dict) and repo.get("id") == repo_id:
                repo["trusted"] = trusted
                found = True
                break

        if not found:
            raise TemplatesConfigError(f"Repo ID '{repo_id}' not found in config.")
        self._sync_templates_config()

    def _require_templates_enabled(self) -> None:
        """Ensure templates are enabled in config."""
        if self._templates_enabled is False:
            raise TemplatesConfigError(
                "Templates are disabled. Set templates.enabled=true in the hub config to enable."
            )


def load_template_repos_manager(hub: Optional[Path]) -> TemplateReposManager:
    """Load a TemplateReposManager for the given hub path."""
    try:
        config = load_hub_config(hub or Path.cwd())
    except Exception as exc:
        typer.echo(str(exc), err=True)
        raise typer.Exit(code=1) from None
    hub_config_path = config.root / CONFIG_FILENAME
    return TemplateReposManager(hub_config_path)
