"""Controller for configuration management in CLI."""

from __future__ import annotations

import subprocess
from pathlib import Path
from typing import TYPE_CHECKING, Any

import git

from portal.interfaces.cli.utils.error_handler import ErrorHandler
from portal.interfaces.cli.utils.input_validator import InputValidator

if TYPE_CHECKING:
    from portal.core.services.config_service import ConfigService
    from portal.interfaces.cli.presenters.output_presenter import OutputPresenter


class ConfigController:
    """Controller for configuration management in CLI."""

    def __init__(self, config_service: ConfigService, output_presenter: OutputPresenter) -> None:
        """Initialize the config controller.

        Args:
            config_service: Service for configuration operations
            output_presenter: Presenter for output formatting
        """
        self.config_service = config_service
        self.output_presenter = output_presenter
        self.error_handler = ErrorHandler(output_presenter)

    async def show_config(self, global_only: bool = False, json_format: bool = False) -> None:
        """Show current configuration.

        Args:
            global_only: Whether to show only global config
            json_format: Whether to output as JSON (for scripts)
        """

        async def show_operation() -> Any:
            if global_only:
                config = await self.config_service.get_global_config()
                title = "Global Configuration"
            else:
                # Get the merged configuration for the current project
                try:
                    repo = git.Repo(Path.cwd(), search_parent_directories=True)
                    repo_root = Path(repo.working_dir)
                    # Force reload configuration for the current project
                    config = await self.config_service.get_config(repo_root)
                except Exception:
                    # Not in a git repo, just get default config
                    config = await self.config_service.get_config()
                title = "Portal Configuration (Merged)"

            if json_format:
                self.output_presenter.show_json(config)
                return config

            # Format the output nicely
            self.output_presenter.show_success(title)

            # Get current Git repository info
            try:
                repo = git.Repo(Path.cwd(), search_parent_directories=True)
                repo_root = Path(repo.working_dir)
                project_name = repo_root.name

                # Show repository context
                self.output_presenter.show_info("\nRepository Context:")
                details = {
                    "Git Root": str(repo_root),
                    "Project Name": project_name,
                    "Current Branch": repo.active_branch.name
                    if not repo.head.is_detached
                    else "(detached HEAD)",
                }
                for key, value in details.items():
                    self.output_presenter.show_info(f"  {key}: {value}")
            except Exception:
                project_name = "(unknown)"
                self.output_presenter.show_warning("Not in a Git repository")

            # Show configuration values
            self.output_presenter.show_info("\nConfiguration Values:")

            # Core settings
            self.output_presenter.show_info("  Core:")
            self.output_presenter.show_info(f"    Version: {config.version}")
            self.output_presenter.show_info(
                f"    Default Base Branch: {config.default_base_branch}"
            )

            # Worktree settings with resolved paths
            self.output_presenter.show_info("  Worktree Settings:")
            self.output_presenter.show_info(f"    Base Directory Template: {config.base_dir}")

            if project_name != "(unknown)":
                resolved_base_dir = config.base_dir.replace("{project}", project_name)
                if resolved_base_dir.startswith("../"):
                    actual_path = (repo_root.parent / resolved_base_dir[3:]).resolve()
                else:
                    actual_path = (repo_root.parent / resolved_base_dir).resolve()
                self.output_presenter.show_info(f"    Resolved Base Directory: {resolved_base_dir}")
                self.output_presenter.show_info(f"    Actual Worktree Path: {actual_path}")

            # Editor settings
            if hasattr(config, "editor") and config.editor:
                self.output_presenter.show_info("  Editor:")
                self.output_presenter.show_info(f"    Default: {config.editor.default}")
                if hasattr(config.editor, "auto_open") and config.editor.auto_open is not None:
                    self.output_presenter.show_info(f"    Auto-open: {config.editor.auto_open}")

            # Color settings
            if hasattr(config, "colors") and config.colors:
                self.output_presenter.show_info("  Colors:")
                self.output_presenter.show_info(f"    Enabled: {config.colors.enabled}")
                if hasattr(config.colors, "sync_iterm"):
                    self.output_presenter.show_info(f"    Sync iTerm: {config.colors.sync_iterm}")
                if hasattr(config.colors, "sync_cursor"):
                    self.output_presenter.show_info(f"    Sync Cursor: {config.colors.sync_cursor}")

            # Hook settings
            if hasattr(config, "hooks") and config.hooks:
                self.output_presenter.show_info("  Hooks:")
                for stage, hooks in config.hooks.items():
                    if hooks:
                        self.output_presenter.show_info(
                            f"    {stage}: {len(hooks)} hook(s) configured"
                        )

            # Show configuration sources
            if not global_only:
                self.output_presenter.show_info("\nConfiguration Sources (in order of priority):")

                # Check for project config
                project_config_path = (
                    repo_root / ".portal.yml"
                    if project_name != "(unknown)"
                    else Path.cwd() / ".portal.yml"
                )
                if project_config_path.exists():
                    self.output_presenter.show_info(f"  1. Project: {project_config_path} ✓")
                else:
                    self.output_presenter.show_info(
                        f"  1. Project: {project_config_path} (not found)"
                    )

                # Check for global config
                global_config_path = Path.home() / ".portal" / "config.yml"
                if global_config_path.exists():
                    self.output_presenter.show_info(f"  2. Global: {global_config_path} ✓")
                else:
                    self.output_presenter.show_info(
                        f"  2. Global: {global_config_path} (not found)"
                    )

                self.output_presenter.show_info("  3. Defaults: Built-in Portal defaults")

            return config

        await self.error_handler.handle_operation(show_operation, "load configuration")

    async def set_config(self, key: str, value: str, is_global: bool = False) -> None:
        """Set configuration value.

        Args:
            key: Configuration key (e.g., 'base_dir')
            value: Value to set
            is_global: Whether to set in global config
        """
        # Validate inputs
        validated_key = InputValidator.validate_config_key(key)
        validated_value = InputValidator.validate_config_value(value)

        if not validated_key:
            self.output_presenter.show_error("Invalid configuration key")
            return

        if validated_value is None:
            self.output_presenter.show_error("Invalid configuration value")
            return

        async def set_operation() -> tuple[Any, str]:
            key_parts = validated_key.split(".")
            scope = "global" if is_global else "local"

            if is_global:
                result = await self.config_service.set_global_config_value(
                    key_parts, validated_value
                )
            else:
                result = await self.config_service.set_config_value(key_parts, validated_value)

            return result, scope

        operation_result = await self.error_handler.handle_operation(
            set_operation, f"set {validated_key} configuration"
        )

        if operation_result:
            result, scope = operation_result
            self.error_handler.handle_service_result(
                result, f"Set {scope} config: {validated_key} = {validated_value}"
            )

    async def edit_config(self, is_global: bool = False) -> None:
        """Edit configuration file in default editor.

        Args:
            is_global: Whether to edit global config file
        """
        try:
            if is_global:
                config_path = await self.config_service.get_global_config_path()
                scope = "global"
            else:
                config_path = await self.config_service.get_local_config_path()
                scope = "local"

            # Get and validate editor
            config = await self.config_service.get_config()
            editor_cmd = getattr(config, "default_editor", "cursor")

            validated_editor = InputValidator.validate_editor_command(editor_cmd)
            if not validated_editor:
                validated_editor = "cursor"  # Safe fallback

            try:
                # Open editor with timeout
                subprocess.run([validated_editor, str(config_path)], check=False, timeout=10)
                self.output_presenter.show_success(f"Opened {scope} config file in {editor_cmd}")
            except subprocess.TimeoutExpired:
                self.output_presenter.show_error(f"Editor '{validated_editor}' timed out")
            except FileNotFoundError:
                # Fallback to system default
                try:
                    import platform

                    opener = "open" if platform.system() == "Darwin" else "xdg-open"
                    subprocess.run([opener, str(config_path)], check=False, timeout=10)
                    self.output_presenter.show_success(
                        f"Opened {scope} config file in default editor"
                    )
                except (FileNotFoundError, subprocess.TimeoutExpired):
                    self.output_presenter.show_error(
                        f"No suitable editor found. Please edit {config_path} manually"
                    )

        except Exception as e:
            self.output_presenter.show_error(f"Failed to edit configuration: {e}")

    async def reset_config(self, is_global: bool = False) -> None:
        """Reset configuration to defaults.

        Args:
            is_global: Whether to reset global config
        """
        try:
            scope = "global" if is_global else "local"

            if is_global:
                await self.config_service.reset_global_config()
            else:
                await self.config_service.reset_local_config()

            self.output_presenter.show_success(f"Reset {scope} configuration to defaults")

        except Exception as e:
            self.output_presenter.show_error(f"Failed to reset configuration: {e}")

    async def validate_config(self) -> None:
        """Validate current configuration."""
        try:
            result = await self.config_service.validate_config()

            if result["success"]:
                self.output_presenter.show_success("Configuration is valid")
            else:
                self.output_presenter.show_error("Configuration validation failed")
                self.output_presenter.show_errors(result["errors"])

            if result["warnings"]:
                self.output_presenter.show_warnings(result["warnings"])

        except Exception as e:
            self.output_presenter.show_error(f"Failed to validate configuration: {e}")
