"""
CLI interface for cl-preset package.
"""

import json
from pathlib import Path

import click
from rich.console import Console
from rich.panel import Panel

from cl_preset.git_strategy import GitStrategyManager
from cl_preset.interactive import InteractiveCLI
from cl_preset.manager import PresetManager
from cl_preset.models import PresetMetadata, PresetScope, PresetType

console = Console()


@click.group(invoke_without_command=True)
@click.version_option(version="0.1.0", prog_name="cl-preset")
@click.pass_context
def main(ctx: click.Context) -> None:
    """cl-preset: Manage Claude Code configuration presets and strategies.

    Running without a command starts the interactive strategy selector.
    """
    # If no subcommand is provided, run interactive mode
    if ctx.invoked_subcommand is None:
        interactive = InteractiveCLI()
        interactive.run()


@main.command()
@click.argument("source_path", type=click.Path(exists=True, path_type=Path))
@click.option("--name", "-n", required=True, help="Unique name for the preset")
@click.option("--version", "-v", default="1.0.0", help="Version (default: 1.0.0)")
@click.option("--description", "-d", required=True, help="Description of the preset")
@click.option("--author", "-a", default="", help="Author name")
@click.option(
    "--type",
    "-t",
    type=click.Choice(["agent", "skill", "command", "strategy"]),
    default="strategy",
    help="Type of preset (default: strategy)",
)
@click.option(
    "--scope",
    "-s",
    type=click.Choice(["global", "user", "project"]),
    default="user",
    help="Installation scope (default: user)",
)
def install(
    source_path: Path, name: str, version: str, description: str, author: str, type: str, scope: str
) -> None:
    """Install a preset from a source directory.

    SOURCE_PATH is the path to the directory containing the preset files.

    Example:
        cl-preset install ./my-strategy --name my-strategy --description "My custom strategy"
    """
    manager = PresetManager()

    metadata = PresetMetadata(
        name=name,
        version=version,
        description=description,
        author=author,
        preset_type=PresetType(type),
    )

    preset = manager.create_from_directory(
        source_path=source_path, metadata=metadata, scope=PresetScope(scope)
    )

    manager.install(preset)


@main.command()
@click.argument("name")
@click.option(
    "--scope", "-s", type=click.Choice(["global", "user", "project"]), help="Filter by scope"
)
def uninstall(name: str, scope: str | None) -> None:
    """Uninstall a preset by name.

    NAME is the name of the preset to uninstall.

    Example:
        cl-preset uninstall my-strategy
    """
    manager = PresetManager()
    scope_enum = PresetScope(scope) if scope else None
    manager.uninstall(name, scope_enum)


@main.command()
@click.option(
    "--scope", "-s", type=click.Choice(["global", "user", "project"]), help="Filter by scope"
)
@click.option("--json", "json_output", is_flag=True, help="Output as JSON")
def list_cmd(scope: str | None, json_output: bool) -> None:
    """List installed presets.

    Example:
        cl-preset list
        cl-preset list --scope user
    """
    manager = PresetManager()
    scope_enum = PresetScope(scope) if scope else None

    if json_output:
        presets = manager.list(scope_enum)
        console.print_json(json.dumps(presets, indent=2))
    else:
        manager.print_list(scope_enum)


@main.command()
@click.argument("name")
@click.option(
    "--output",
    "-o",
    type=click.Path(path_type=Path),
    default=None,
    help="Output path for preset metadata",
)
def init(name: str, output: Path | None) -> None:
    """Initialize a new preset scaffold.

    Creates a template directory structure for a new preset.

    NAME is the name of the preset to create.

    Example:
        cl-preset init my-strategy
    """
    if output is None:
        output = Path.cwd() / "preset.json"

    preset_dir = Path.cwd() / name
    preset_dir.mkdir(exist_ok=True)

    # Create directory structure
    (preset_dir / "agents").mkdir(exist_ok=True)
    (preset_dir / "skills").mkdir(exist_ok=True)
    (preset_dir / "commands").mkdir(exist_ok=True)
    (preset_dir / "examples").mkdir(exist_ok=True)

    # Create README template
    readme_content = f"""# {name}

## Description

Add your description here.

## Installation

```bash
cl-preset install . --name {name} --description "Your description"
```

## Structure

- `agents/` - Agent definitions
- `skills/` - Skill definitions
- `commands/` - Command definitions
- `examples/` - Usage examples

## Usage

After installation, the preset will be available in your Claude Code environment.

## Author

Add your name here
"""
    (preset_dir / "README.md").write_text(readme_content)

    # Create preset.json metadata template
    metadata = {
        "name": name,
        "version": "1.0.0",
        "description": "Add your description here",
        "author": "",
        "license": "MIT",
        "preset_type": "strategy",
        "tags": [],
    }
    output.write_text(json.dumps(metadata, indent=2))

    console.print(f"[green]Created preset scaffold at {preset_dir}[/green]")
    console.print(f"[cyan]Metadata written to {output}[/cyan]")
    console.print("\nEdit the metadata and preset files, then install with:")
    console.print(
        f"  cl-preset install {preset_dir} --name {name} --description 'Your description'"
    )


@main.command()
@click.argument("name")
def info(name: str) -> None:
    """Show detailed information about an installed preset.

    NAME is the name of the preset.

    Example:
        cl-preset info my-strategy
    """
    manager = PresetManager()
    registry = manager._load_registry()

    installed = registry.get("installed", [])
    preset_info = None

    for preset in installed:
        if preset["name"] == name:
            preset_info = preset
            break

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

    # Try to load detailed metadata
    preset_path = Path(preset_info["path"])
    metadata_path = preset_path / "preset.json"

    if metadata_path.exists():
        metadata = json.loads(metadata_path.read_text())
        console.print(f"[cyan]Name:[/cyan] {metadata.get('name', 'N/A')}")
        console.print(f"[cyan]Version:[/cyan] {metadata.get('version', 'N/A')}")
        console.print(f"[cyan]Description:[/cyan] {metadata.get('description', 'N/A')}")
        console.print(f"[cyan]Author:[/cyan] {metadata.get('author', 'N/A')}")
        console.print(f"[cyan]License:[/cyan] {metadata.get('license', 'N/A')}")
        console.print(f"[cyan]Type:[/cyan] {metadata.get('preset_type', 'N/A')}")
        console.print(f"[cyan]Tags:[/cyan] {', '.join(metadata.get('tags', [])) or 'None'}")
    else:
        console.print(f"[cyan]Name:[/cyan] {preset_info['name']}")
        console.print(f"[cyan]Version:[/cyan] {preset_info['version']}")
        console.print(f"[cyan]Scope:[/cyan] {preset_info['scope']}")
        console.print(f"[cyan]Path:[/cyan] {preset_info['path']}")


# ============================================================================
# Git Strategy Commands
# ============================================================================


@click.group()
def git() -> None:
    """Git strategy management commands."""
    pass


@git.command("list")
@click.option("--builtin/--no-builtin", default=True, help="Include built-in strategies")
def git_list(builtin: bool) -> None:
    """List available git strategies.

    Example:
        cl-preset git list
        cl-preset git list --no-builtin
    """
    manager = GitStrategyManager()
    manager.print_list(include_builtin=builtin)


@git.command("info")
@click.argument("name")
def git_info(name: str) -> None:
    """Show detailed information about a git strategy.

    NAME is the name of the strategy.

    Example:
        cl-preset git info dual-repo
    """
    manager = GitStrategyManager()
    strategy = manager.get_strategy(name)

    if strategy is None:
        console.print(f"[red]Strategy '{name}' not found.[/red]")
        return

    # Handle strategy_type being either a string or enum (due to use_enum_values=True)
    strategy_type = (
        strategy.strategy_type
        if isinstance(strategy.strategy_type, str)
        else strategy.strategy_type.value
    )

    console.print(Panel.fit(f"[bold cyan]{strategy.name}[/bold cyan]", title="Strategy"))
    console.print(f"[cyan]Type:[/cyan] {strategy_type}")
    console.print(f"[cyan]Description:[/cyan] {strategy.description}")
    console.print()

    console.print("[bold]Branch Configuration:[/bold]")
    console.print(f"  Main Branch: [green]{strategy.main_branch}[/green]")
    console.print(f"  Develop Branch: [green]{strategy.develop_branch}[/green]")
    console.print()

    console.print("[bold]Branch Patterns:[/bold]")
    console.print(f"  Feature: [green]{strategy.branch_patterns.feature}[/green]")
    console.print(f"  Bugfix: [green]{strategy.branch_patterns.bugfix}[/green]")
    console.print(f"  Release: [green]{strategy.branch_patterns.release}[/green]")
    console.print(f"  Hotfix: [green]{strategy.branch_patterns.hotfix}[/green]")
    console.print()

    console.print("[bold]Merge Rules:[/bold]")
    console.print(f"  Require PR: [green]{strategy.merge_rules.require_pr}[/green]")
    console.print(f"  Require Approval: [green]{strategy.merge_rules.require_approval}[/green]")
    console.print(f"  Allow Squash: [green]{strategy.merge_rules.allow_squash}[/green]")
    console.print(f"  Allow Merge Commit: [green]{strategy.merge_rules.allow_merge_commit}[/green]")
    console.print(f"  Allow Rebase: [green]{strategy.merge_rules.allow_rebase}[/green]")
    console.print()

    if strategy.protection_rules:
        console.print("[bold]Protection Rules:[/bold]")
        for branch, rules in strategy.protection_rules.items():
            status = "[green]enabled[/green]" if rules.enabled else "[red]disabled[/red]"
            console.print(f"  {branch}: {status}")
            if rules.enabled:
                if rules.required_check_names:
                    console.print(f"    Required checks: {', '.join(rules.required_check_names)}")

    if strategy.dev_repository:
        console.print()
        console.print("[bold]Dev Repository:[/bold]")
        for key, value in strategy.dev_repository.items():
            console.print(f"  {key}: [green]{value}[/green]")

    if strategy.release_repository:
        console.print()
        console.print("[bold]Release Repository:[/bold]")
        for key, value in strategy.release_repository.items():
            console.print(f"  {key}: [green]{value}[/green]")


@git.command("export")
@click.argument("name")
@click.option(
    "--output",
    "-o",
    type=click.Path(path_type=Path),
    default=None,
    help="Output path (default: ./<name>.yaml)",
)
def git_export(name: str, output: Path | None) -> None:
    """Export a git strategy to YAML file.

    NAME is the name of the strategy to export.

    Example:
        cl-preset git export dual-repo
        cl-preset git export dual-repo -o ./my-strategy.yaml
    """
    manager = GitStrategyManager()

    if output is None:
        output = Path.cwd() / f"{name}.yaml"

    success = manager.export_strategy_yaml(name, output)
    if not success:
        raise click.ClickException(f"Failed to export strategy '{name}'")


@git.command("apply")
@click.argument("name")
@click.option("--dry-run", is_flag=True, help="Show what would be done without making changes")
def git_apply(name: str, dry_run: bool) -> None:
    """Apply a git strategy to the current project.

    NAME is the name of the strategy to apply.

    Example:
        cl-preset git apply github-flow
        cl-preset git apply github-flow --dry-run
    """
    import subprocess

    manager = GitStrategyManager()
    strategy = manager.get_strategy(name)

    if strategy is None:
        console.print(f"[red]Strategy '{name}' not found.[/red]")
        raise click.Abort()

    console.print(f"[cyan]Applying strategy: {strategy.name}[/cyan]")
    console.print(f"[dim]{strategy.description}[/dim]")
    console.print()

    if dry_run:
        console.print("[yellow]Dry run mode - no changes will be made[/yellow]")
        console.print()

    # Show what would be configured
    console.print("[bold]Branch Configuration:[/bold]")
    console.print(f"  Main branch: [green]{strategy.main_branch}[/green]")
    if strategy.develop_branch:
        console.print(f"  Develop branch: [green]{strategy.develop_branch}[/green]")
    console.print()

    console.print("[bold]Branch Patterns:[/bold]")
    console.print(f"  Feature: [green]{strategy.branch_patterns.feature}[/green]")
    console.print(f"  Bugfix: [green]{strategy.branch_patterns.bugfix}[/green]")
    console.print(f"  Release: [green]{strategy.branch_patterns.release}[/green]")
    console.print(f"  Hotfix: [green]{strategy.branch_patterns.hotfix}[/green]")
    console.print()

    if not dry_run:
        # Check if we're in a git repository
        try:
            result = subprocess.run(
                ["git", "rev-parse", "--git-dir"], capture_output=True, text=True, cwd=Path.cwd()
            )
            if result.returncode != 0:
                console.print(
                    "[red]Not in a git repository. Initialize one first with 'git init'[/red]"
                )
                raise click.Abort()

            # Create .moai/config/sections directory
            config_dir = Path.cwd() / ".moai" / "config" / "sections"
            config_dir.mkdir(parents=True, exist_ok=True)

            # Export strategy config
            strategy_file = config_dir / "git-strategy.yaml"
            success = manager.export_strategy_yaml(name, strategy_file)

            if success:
                console.print(f"[green]Strategy configuration written to {strategy_file}[/green]")
                console.print()
                console.print("[bold]Next Steps:[/bold]")
                console.print("  1. Review the strategy configuration")
                console.print("  2. Create required branches if needed")
                console.print("  3. Configure branch protection rules in your Git host")

        except Exception as e:
            console.print(f"[red]Error applying strategy: {e}[/red]")
            raise click.Abort()


@git.command("validate")
@click.argument("name")
def git_validate(name: str) -> None:
    """Validate current git setup against a strategy.

    NAME is the name of the expected strategy.

    Example:
        cl-preset git validate github-flow
    """
    manager = GitStrategyManager()
    results = manager.validate_current_setup(name)

    console.print(f"[cyan]Validating against strategy: {name}[/cyan]")
    console.print()

    if results.get("info"):
        info = results["info"]
        if "current_branch" in info:
            console.print(f"[dim]Current branch: {info['current_branch']}[/dim]")

    console.print()

    if results["valid"]:
        console.print("[green]Validation passed![/green]")
    else:
        console.print("[red]Validation failed![/red]")

    if results["errors"]:
        console.print()
        console.print("[bold red]Errors:[/bold red]")
        for error in results["errors"]:
            console.print(f"  [red]x[/red] {error}")

    if results["warnings"]:
        console.print()
        console.print("[bold yellow]Warnings:[/bold yellow]")
        for warning in results["warnings"]:
            console.print(f"  [yellow]![/yellow] {warning}")

    if not results["errors"] and not results["warnings"]:
        console.print("[dim]No issues found.[/dim]")


# Add git group to main
main.add_command(git)

# Rename list command to avoid conflict with Python's list built-in
main.add_command(list_cmd, name="list")

if __name__ == "__main__":
    main()
