"""
Git strategy models and management for cl-preset package.
"""

from enum import Enum
from pathlib import Path
from typing import Any

from pydantic import BaseModel, ConfigDict, Field
from rich.console import Console
from rich.table import Table


class GitStrategyType(str, Enum):
    """Types of git strategies."""
    DUAL_REPO = "dual-repo"
    GITHUB_FLOW = "github-flow"
    GIT_FLOW = "git-flow"
    CUSTOM = "custom"


class BranchPattern(BaseModel):
    """Branch naming pattern."""
    feature: str = Field(default="feature/*", description="Feature branch pattern")
    bugfix: str = Field(default="bugfix/*", description="Bugfix branch pattern")
    release: str = Field(default="release/*", description="Release branch pattern")
    hotfix: str = Field(default="hotfix/*", description="Hotfix branch pattern")


class MergeRule(BaseModel):
    """Merge rules for branches."""
    require_pr: bool = Field(default=True, description="Require pull request")
    require_approval: bool = Field(default=True, description="Require approval")
    allow_squash: bool = Field(default=True, description="Allow squash merge")
    allow_merge_commit: bool = Field(default=False, description="Allow merge commit")
    allow_rebase: bool = Field(default=False, description="Allow rebase merge")


class ProtectionRule(BaseModel):
    """Branch protection rules."""
    enabled: bool = Field(default=True, description="Enable protection")
    require_status_checks: bool = Field(default=True, description="Require status checks")
    require_branch_up_to_date: bool = Field(default=True, description="Require branch up to date")
    required_check_names: list[str] = Field(
        default_factory=list,
        description="Required status check names"
    )
    allow_force_pushes: bool = Field(default=False, description="Allow force pushes")
    allow_deletions: bool = Field(default=False, description="Allow branch deletions")


class GitStrategyConfig(BaseModel):
    """Git strategy configuration."""
    name: str = Field(..., description="Strategy name")
    strategy_type: GitStrategyType = Field(..., description="Strategy type")
    description: str = Field(..., description="Strategy description")

    # Branch configuration
    main_branch: str = Field(default="main", description="Main branch name")
    develop_branch: str = Field(default="develop", description="Develop branch name (for git-flow)")
    branch_patterns: BranchPattern = Field(
        default_factory=BranchPattern,
        description="Branch naming patterns"
    )

    # Merge and protection rules
    merge_rules: MergeRule = Field(
        default_factory=MergeRule,
        description="Merge rules"
    )
    protection_rules: dict[str, ProtectionRule] = Field(
        default_factory=dict,
        description="Protection rules per branch"
    )

    # Dual-repo specific - use dict[str, Any] to allow flexible structures
    dev_repository: dict[str, Any] | None = Field(
        default=None,
        description="Dev repository configuration (for dual-repo)"
    )
    release_repository: dict[str, Any] | None = Field(
        default=None,
        description="Release repository configuration (for dual-repo)"
    )

    model_config = ConfigDict(use_enum_values=True)


class GitStrategyManager:
    """Manager for git strategy operations."""

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

        Args:
            base_path: Base path for strategy templates. Defaults to package templates
        """
        self.console = Console()
        self.base_path = base_path or (Path(__file__).parent / "templates" / "git_strategies")
        self.strategies_path = Path.home() / ".cl-preset" / "git-strategies"
        self.strategies_path.mkdir(parents=True, exist_ok=True)

    def get_builtin_strategies(self) -> dict[str, GitStrategyConfig]:
        """Get built-in git strategy definitions."""
        return {
            "dual-repo": self._get_dual_repo_strategy(),
            "github-flow": self._get_github_flow_strategy(),
            "git-flow": self._get_git_flow_strategy(),
        }

    def _get_dual_repo_strategy(self) -> GitStrategyConfig:
        """Get dual-repo strategy configuration."""
        protection_rules = {
            "main": ProtectionRule(
                enabled=True,
                require_status_checks=True,
                require_branch_up_to_date=True,
                required_check_names=["ci", "tests", "lint"],
                allow_force_pushes=False,
                allow_deletions=False,
            ),
            "release/*": ProtectionRule(
                enabled=True,
                require_status_checks=True,
                require_branch_up_to_date=True,
                required_check_names=["ci", "tests"],
                allow_force_pushes=False,
                allow_deletions=True,
            ),
        }

        return GitStrategyConfig(
            name="dual-repo",
            strategy_type=GitStrategyType.DUAL_REPO,
            description="Dev/Release repository separation workflow. Dev repo for active development, release repo for stable releases.",
            main_branch="main",
            develop_branch="develop",
            branch_patterns=BranchPattern(
                feature="feature/*",
                bugfix="bugfix/*",
                release="release/*",
                hotfix="hotfix/*",
            ),
            merge_rules=MergeRule(
                require_pr=True,
                require_approval=True,
                allow_squash=True,
                allow_merge_commit=False,
                allow_rebase=False,
            ),
            protection_rules=protection_rules,
            dev_repository={
                "remote": "origin",
                "branch_pattern": "feature/*",
                "description": "Active development, feature branches, experimental work",
            },
            release_repository={
                "remote": "release",
                "protected_branches": ["main"],
                "description": "Production-ready code, stable releases, published documentation",
            },
        )

    def _get_github_flow_strategy(self) -> GitStrategyConfig:
        """Get GitHub flow strategy configuration."""
        protection_rules = {
            "main": ProtectionRule(
                enabled=True,
                require_status_checks=True,
                require_branch_up_to_date=True,
                required_check_names=["ci"],
                allow_force_pushes=False,
                allow_deletions=False,
            ),
        }

        return GitStrategyConfig(
            name="github-flow",
            strategy_type=GitStrategyType.GITHUB_FLOW,
            description="Simple branch-based workflow. Feature branches from main, PR for review, merge after approval.",
            main_branch="main",
            branch_patterns=BranchPattern(
                feature="feature/*",
                bugfix="bugfix/*",
                release="",
                hotfix="hotfix/*",
            ),
            merge_rules=MergeRule(
                require_pr=True,
                require_approval=True,
                allow_squash=True,
                allow_merge_commit=True,
                allow_rebase=True,
            ),
            protection_rules=protection_rules,
        )

    def _get_git_flow_strategy(self) -> GitStrategyConfig:
        """Get Git flow strategy configuration."""
        protection_rules = {
            "master": ProtectionRule(
                enabled=True,
                require_status_checks=True,
                require_branch_up_to_date=True,
                required_check_names=["ci", "tests"],
                allow_force_pushes=False,
                allow_deletions=False,
            ),
            "develop": ProtectionRule(
                enabled=True,
                require_status_checks=True,
                require_branch_up_to_date=False,
                required_check_names=["ci"],
                allow_force_pushes=False,
                allow_deletions=False,
            ),
        }

        return GitStrategyConfig(
            name="git-flow",
            strategy_type=GitStrategyType.GIT_FLOW,
            description="Feature/release/hotfix branching model. Master, develop, feature, release, hotfix branches with strict merge rules.",
            main_branch="master",
            develop_branch="develop",
            branch_patterns=BranchPattern(
                feature="feature/*",
                bugfix="bugfix/*",
                release="release/*",
                hotfix="hotfix/*",
            ),
            merge_rules=MergeRule(
                require_pr=True,
                require_approval=True,
                allow_squash=True,
                allow_merge_commit=True,
                allow_rebase=False,
            ),
            protection_rules=protection_rules,
        )

    def list_strategies(self, include_builtin: bool = True) -> list[dict]:
        """
        List available strategies.

        Args:
            include_builtin: Include built-in strategies

        Returns:
            List of strategy information
        """
        strategies = []

        if include_builtin:
            for name, config in self.get_builtin_strategies().items():
                # strategy_type is already a string due to use_enum_values=True
                strategy_type_value = (
                    config.strategy_type if isinstance(config.strategy_type, str)
                    else config.strategy_type.value
                )
                strategies.append({
                    "name": name,
                    "type": strategy_type_value,
                    "description": config.description,
                    "source": "builtin",
                })

        # List custom strategies
        for strategy_file in self.strategies_path.glob("*.yaml"):
            try:
                import yaml
                data = yaml.safe_load(strategy_file.read_text())
                strategies.append({
                    "name": data.get("name", strategy_file.stem),
                    "type": data.get("strategy_type", "custom"),
                    "description": data.get("description", ""),
                    "source": "custom",
                })
            except Exception:
                pass

        return strategies

    def print_list(self, include_builtin: bool = True) -> None:
        """Print a formatted table of available strategies."""
        strategies = self.list_strategies(include_builtin)

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

        table = Table(title="Available Git Strategies")
        table.add_column("Name", style="cyan")
        table.add_column("Type", style="green")
        table.add_column("Description", style="white")
        table.add_column("Source", style="yellow")

        for strategy in strategies:
            table.add_row(
                strategy["name"],
                strategy["type"],
                strategy["description"][:60] + "..." if len(strategy["description"]) > 60 else strategy["description"],
                strategy["source"],
            )

        self.console.print(table)

    def get_strategy(self, name: str) -> GitStrategyConfig | None:
        """
        Get a strategy by name.

        Args:
            name: Strategy name

        Returns:
            GitStrategyConfig if found, None otherwise
        """
        builtin = self.get_builtin_strategies()
        if name in builtin:
            return builtin[name]

        # Try to load custom strategy
        strategy_file = self.strategies_path / f"{name}.yaml"
        if strategy_file.exists():
            try:
                import yaml
                data = yaml.safe_load(strategy_file.read_text())
                return GitStrategyConfig(**data)
            except Exception as e:
                self.console.print(f"[red]Error loading strategy '{name}': {e}[/red]")
                return None

        return None

    def save_strategy(self, strategy: GitStrategyConfig, name: str) -> bool:
        """
        Save a custom strategy.

        Args:
            strategy: Strategy configuration to save
            name: Name for the strategy

        Returns:
            True if successful, False otherwise
        """
        try:
            import yaml
            strategy_file = self.strategies_path / f"{name}.yaml"
            strategy_file.write_text(
                yaml.dump(strategy.model_dump(mode="json"), default_flow_style=False)
            )
            self.console.print(f"[green]Strategy '{name}' saved successfully.[/green]")
            return True
        except Exception as e:
            self.console.print(f"[red]Error saving strategy: {e}[/red]")
            return False

    def export_strategy_yaml(self, name: str, output_path: Path) -> bool:
        """
        Export a strategy to YAML file.

        Args:
            name: Strategy name
            output_path: Output file path

        Returns:
            True if successful, False otherwise
        """
        strategy = self.get_strategy(name)
        if strategy is None:
            self.console.print(f"[red]Strategy '{name}' not found.[/red]")
            return False

        try:
            import yaml
            output_path.parent.mkdir(parents=True, exist_ok=True)
            output_path.write_text(
                yaml.dump(strategy.model_dump(mode="json"), default_flow_style=False, sort_keys=False)
            )
            self.console.print(f"[green]Strategy exported to {output_path}[/green]")
            return True
        except Exception as e:
            self.console.print(f"[red]Error exporting strategy: {e}[/red]")
            return False

    def validate_current_setup(self, expected_strategy: str) -> dict:
        """
        Validate current git setup against a strategy.

        Args:
            expected_strategy: Name of the expected strategy

        Returns:
            Validation results
        """
        import subprocess

        strategy = self.get_strategy(expected_strategy)
        if strategy is None:
            return {"valid": False, "errors": [f"Strategy '{expected_strategy}' not found"]}

        errors = []
        warnings = []
        info = {}

        try:
            # Check if we're in a git repository
            result = subprocess.run(
                ["git", "rev-parse", "--git-dir"],
                capture_output=True,
                text=True,
                cwd=Path.cwd()
            )
            if result.returncode != 0:
                errors.append("Not in a git repository")
                return {"valid": False, "errors": errors}

            # Check current branch
            result = subprocess.run(
                ["git", "branch", "--show-current"],
                capture_output=True,
                text=True,
                cwd=Path.cwd()
            )
            current_branch = result.stdout.strip()
            info["current_branch"] = current_branch

            # Check remotes
            result = subprocess.run(
                ["git", "remote", "-v"],
                capture_output=True,
                text=True,
                cwd=Path.cwd()
            )
            remotes = result.stdout.strip()
            info["remotes"] = remotes

            # Check if main branch exists
            result = subprocess.run(
                ["git", "branch", "-a", "--list", strategy.main_branch],
                capture_output=True,
                text=True,
                cwd=Path.cwd()
            )
            if strategy.main_branch not in result.stdout:
                warnings.append(f"Main branch '{strategy.main_branch}' not found")

            # For git-flow, check develop branch
            if strategy.strategy_type == GitStrategyType.GIT_FLOW:
                result = subprocess.run(
                    ["git", "branch", "-a", "--list", strategy.develop_branch],
                    capture_output=True,
                    text=True,
                    cwd=Path.cwd()
                )
                if strategy.develop_branch not in result.stdout:
                    warnings.append(f"Develop branch '{strategy.develop_branch}' not found")

            # For dual-repo, check remotes
            if strategy.strategy_type == GitStrategyType.DUAL_REPO:
                if strategy.dev_repository:
                    dev_remote = strategy.dev_repository.get("remote", "origin")
                    if dev_remote not in remotes:
                        warnings.append(f"Dev remote '{dev_remote}' not configured")
                if strategy.release_repository:
                    release_remote = strategy.release_repository.get("remote", "release")
                    if release_remote not in remotes:
                        warnings.append(f"Release remote '{release_remote}' not configured")

        except Exception as e:
            errors.append(f"Validation error: {e}")

        return {
            "valid": len(errors) == 0,
            "errors": errors,
            "warnings": warnings,
            "info": info,
        }
