"""
Preset manager for handling installation, listing, and removal of presets.
"""

import json
import shutil
from pathlib import Path

from rich.console import Console
from rich.table import Table

from cl_preset.models import Preset, PresetConfig, PresetMetadata, PresetScope


class PresetManager:
    """Manager for preset operations."""

    def __init__(self, base_path: Path | None = None):
        """
        Initialize the preset manager.

        Args:
            base_path: Base path for global installations. Defaults to ~/.claude/presets
        """
        self.console = Console()
        self.base_path = base_path or (Path.home() / ".claude" / "presets")
        self.registry_path = self.base_path / "registry.json"
        self._ensure_registry()

    def _ensure_registry(self) -> None:
        """Ensure the registry file exists."""
        self.base_path.mkdir(parents=True, exist_ok=True)
        if not self.registry_path.exists():
            self.registry_path.write_text(json.dumps({"installed": []}, indent=2))

    def _load_registry(self) -> dict:
        """Load the installed presets registry."""
        return json.loads(self.registry_path.read_text())

    def _save_registry(self, registry: dict) -> None:
        """Save the registry to disk."""
        self.registry_path.write_text(json.dumps(registry, indent=2))

    def install(self, preset: Preset) -> bool:
        """
        Install a preset.

        Args:
            preset: The preset to install

        Returns:
            True if successful, False otherwise
        """
        install_path = preset.config.get_install_path(self.base_path)

        # Check if already installed
        registry = self._load_registry()
        installed_names = [p["name"] for p in registry["installed"]]

        if preset.config.metadata.name in installed_names:
            self.console.print(f"[yellow]Preset '{preset.config.metadata.name}' is already installed.[/yellow]")
            return False

        # Create installation directory
        install_path.mkdir(parents=True, exist_ok=True)

        # Copy files
        source_path = preset.config.source_path
        if source_path.exists():
            if source_path.is_dir():
                shutil.copytree(source_path, install_path, dirs_exist_ok=True)
            else:
                shutil.copy2(source_path, install_path / source_path.name)

        # Write metadata
        metadata_path = install_path / "preset.json"
        metadata_path.write_text(
            json.dumps(preset.config.metadata.model_dump(mode="json"), indent=2)
        )

        # Update registry
        registry["installed"].append({
            "name": preset.config.metadata.name,
            "version": preset.config.metadata.version,
            "scope": preset.config.scope.value,
            "path": str(install_path),
        })
        self._save_registry(registry)

        self.console.print(f"[green]Successfully installed '{preset.config.metadata.name}'[/green]")
        return True

    def uninstall(self, name: str, scope: PresetScope | None = None) -> bool:
        """
        Uninstall a preset.

        Args:
            name: Name of the preset to uninstall
            scope: Optional scope to filter by

        Returns:
            True if successful, False otherwise
        """
        registry = self._load_registry()

        # Find the preset
        installed = registry.get("installed", [])
        preset_entry = None
        preset_index = -1

        for i, preset in enumerate(installed):
            if preset["name"] == name:
                if scope is None or preset["scope"] == scope.value:
                    preset_entry = preset
                    preset_index = i
                    break

        if preset_entry is None:
            self.console.print(f"[red]Preset '{name}' not found.[/red]")
            return False

        # Remove files
        preset_path = Path(preset_entry["path"])
        if preset_path.exists():
            shutil.rmtree(preset_path)

        # Update registry
        installed.pop(preset_index)
        registry["installed"] = installed
        self._save_registry(registry)

        self.console.print(f"[green]Successfully uninstalled '{name}'[/green]")
        return True

    def list(self, scope: PresetScope | None = None) -> list[dict]:
        """
        List installed presets.

        Args:
            scope: Optional scope to filter by

        Returns:
            List of installed preset information
        """
        registry = self._load_registry()
        installed = registry.get("installed", [])

        if scope:
            installed = [p for p in installed if p["scope"] == scope.value]

        return installed

    def print_list(self, scope: PresetScope | None = None) -> None:
        """Print a formatted table of installed presets."""
        presets = self.list(scope)

        if not presets:
            self.console.print("[yellow]No presets installed.[/yellow]")
            return

        table = Table(title="Installed Presets")
        table.add_column("Name", style="cyan")
        table.add_column("Version", style="green")
        table.add_column("Scope", style="yellow")
        table.add_column("Path", style="blue")

        for preset in presets:
            table.add_row(
                preset["name"],
                preset["version"],
                preset["scope"],
                preset["path"][:50] + "..." if len(preset["path"]) > 50 else preset["path"]
            )

        self.console.print(table)

    def create_from_directory(
        self,
        source_path: Path,
        metadata: PresetMetadata,
        scope: PresetScope = PresetScope.USER
    ) -> Preset:
        """
        Create a preset from a directory.

        Args:
            source_path: Path to the source directory
            metadata: Metadata for the preset
            scope: Installation scope

        Returns:
            A Preset object
        """
        config = PresetConfig(
            metadata=metadata,
            source_path=source_path,
            scope=scope
        )
        return Preset(config=config)
