"""
Rich-based output formatting and reporting.
"""

import json
import shlex
from pathlib import Path
from typing import Any

from persuade.constants import GITIGNORE_EXPLANATIONS

# Type checking for Console
try:
    from rich.console import Console as ConsoleType
except ImportError:
    ConsoleType = None  # type: ignore

try:
    from rich.console import Console
    from rich.panel import Panel
    from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn
    from rich.table import Table

    RICH_AVAILABLE = True
except ImportError:
    RICH_AVAILABLE = False


class SyncReporter:
    """Beautiful terminal output using Rich."""

    def __init__(self, verbose: bool = False) -> None:
        self.verbose = verbose
        self.console: Console | None
        if RICH_AVAILABLE:
            self.console = Console()
        else:
            self.console = None

    def print_diff(self, changes: Any, settings_diff: dict[str, Any]) -> None:
        """
        Display comprehensive diff of changes.

        Args:
            changes: ChangeSet with file changes
            settings_diff: Dictionary with permission/hook changes
        """
        if RICH_AVAILABLE and self.console:
            # Files to be added
            if changes.added:
                self.console.print("\n[bold green]Files to be added:[/bold green]")
                for file_path in changes.added:
                    self.console.print(f"  [green]+[/green] {file_path}")

            # Files to be modified
            if changes.modified:
                self.console.print("\n[bold yellow]Files to be modified:[/bold yellow]")
                for file_path in changes.modified:
                    self.console.print(f"  [yellow]~[/yellow] {file_path}")

            # Files to be removed (only in strict mode)
            if changes.removed:
                self.console.print("\n[bold red]Files to be removed:[/bold red]")
                for file_path in changes.removed:
                    self.console.print(f"  [red]-[/red] {file_path}")

            # Settings.json merge details
            if settings_diff:
                self.console.print("\n[bold cyan]settings.json changes:[/bold cyan]")

                # Permissions added
                if settings_diff.get("permissions_added"):
                    self.console.print("\n  [green]Permissions added:[/green]")
                    for perm in settings_diff["permissions_added"]:
                        self.console.print(f"    [green]+[/green] {perm}")

                # Permissions removed (strict mode only)
                if settings_diff.get("permissions_removed"):
                    self.console.print("\n  [red]Permissions removed:[/red]")
                    for perm in settings_diff["permissions_removed"]:
                        self.console.print(f"    [red]-[/red] {perm}")

                # Hooks added
                if settings_diff.get("hooks_added"):
                    self.console.print("\n  [green]Hooks added:[/green]")
                    for hook in settings_diff["hooks_added"]:
                        self.console.print(f"    [green]+[/green] {hook}")

            # Summary table
            self.print_summary_table(changes, settings_diff)
        else:
            # Fallback plain text output
            if changes.added:
                print("\nFiles to be added:")
                for file_path in changes.added:
                    print(f"  + {file_path}")

            if changes.modified:
                print("\nFiles to be modified:")
                for file_path in changes.modified:
                    print(f"  ~ {file_path}")

            if settings_diff.get("permissions_added"):
                print("\nPermissions added:")
                for perm in settings_diff["permissions_added"]:
                    print(f"  + {perm}")

    def print_summary_table(self, changes: Any, settings_diff: dict[str, Any]) -> None:
        """Print summary statistics table."""
        if RICH_AVAILABLE and self.console:
            table = Table(title="Sync Summary", show_header=True)

            table.add_column("Category", style="cyan")
            table.add_column("Count", justify="right", style="magenta")

            table.add_row("Files added", str(len(changes.added)))
            table.add_row("Files modified", str(len(changes.modified)))
            table.add_row("Files removed", str(len(changes.removed)))
            table.add_row(
                "Permissions added",
                str(len(settings_diff.get("permissions_added", []))),
            )
            table.add_row(
                "Permissions removed",
                str(len(settings_diff.get("permissions_removed", []))),
            )
            table.add_row("Hooks added", str(len(settings_diff.get("hooks_added", []))))

            self.console.print("\n")
            self.console.print(table)
        else:
            print("\nSync Summary:")
            print(f"  Files added: {len(changes.added)}")
            print(f"  Files modified: {len(changes.modified)}")
            print(f"  Permissions added: {len(settings_diff.get('permissions_added', []))}")

    def print_success(
        self,
        message: str,
        details: dict[str, Any] | None = None,
        gitignore_additions: list[str] | None = None,
        gitignore_violations: list[str] | None = None,
        target_path: Path | None = None,
        shadow_mode_enabled: bool = False,
        shadow_untrack_commands: list[str] | None = None,
    ) -> None:
        """Print success message with optional gitignore warnings and shadow mode info."""
        if RICH_AVAILABLE and self.console:
            self.console.print(f"\n[bold green]✓[/bold green] {message}")

            if details and self.verbose:
                self.console.print("\n[dim]Details:[/dim]")
                for key, value in details.items():
                    self.console.print(f"  [dim]{key}:[/dim] {value}")
        else:
            print(f"\n✓ {message}")
            if details and self.verbose:
                print("\nDetails:")
                for key, value in details.items():
                    print(f"  {key}: {value}")

        # Shadow mode notification
        if shadow_mode_enabled:
            print("\n🌑 Shadow mode enabled!")
            print("   .claude/ files synced locally but excluded from git\n")

            if gitignore_additions:
                print("  Added to .git/info/exclude:")
                for entry in gitignore_additions:
                    print(f"    + {entry}")
                print()

            if shadow_untrack_commands:
                print("  ⚠  Found files already tracked in git")
                print("  To untrack them, run:")
                for cmd in shadow_untrack_commands:
                    print(f"    {cmd}")
                print()

        # Warn about tracked files that should be ignored
        if gitignore_violations and not shadow_mode_enabled:
            print("\n⚠  Warning: These files should be ignored but are tracked in git:")
            for file in gitignore_violations:
                print(f"  - {file}")
            print("\n  To untrack these files:")
            for file in gitignore_violations:
                print(f"    git rm --cached {shlex.quote(file)}")
            print()

        # Guide user to commit .gitignore changes
        if gitignore_additions and not shadow_mode_enabled:
            print("\n⚠  IMPORTANT: Commit .gitignore changes (best practice)\n")
            count = len(gitignore_additions)
            entry_word = "entry" if count == 1 else "entries"
            print(f"  {count} {entry_word} added to .gitignore:")

            # Explain each entry
            for entry in gitignore_additions:
                explanation = GITIGNORE_EXPLANATIONS.get(entry, "")
                if explanation:
                    print(f"    + {entry:<30} ({explanation})")
                else:
                    print(f"    + {entry}")

            print("\n  These files should NEVER be committed to version control.\n")
            commit_msg = "chore: add Claude Code entries to gitignore"
            if target_path:
                escaped_path = shlex.quote(str(target_path))
                print(f"  Run: git -C {escaped_path} add .gitignore && ")
                print(f'       git -C {escaped_path} commit -m "{commit_msg}"\n')
            else:
                print(f'  Run: git add .gitignore && git commit -m "{commit_msg}"\n')

    def print_error(self, message: str, exception: Exception | None = None) -> None:
        """Print error message."""
        if RICH_AVAILABLE and self.console:
            self.console.print(f"\n[bold red]✗[/bold red] {message}", style="red")

            if exception and self.verbose:
                self.console.print_exception()
        else:
            print(f"\n✗ {message}")

    def print_warning(
        self,
        message: str,
        details: dict[str, Any] | None = None,
    ) -> None:
        """Print warning message with optional details."""
        if RICH_AVAILABLE and self.console:
            self.console.print(f"\n[bold yellow]⚠[/bold yellow] {message}", style="yellow")

            if details and self.verbose:
                self.console.print("\n[dim]Details:[/dim]")
                for key, value in details.items():
                    self.console.print(f"  [dim]{key}:[/dim] {value}")
        else:
            print(f"\n⚠ {message}")
            if details and self.verbose:
                print("\nDetails:")
                for key, value in details.items():
                    print(f"  {key}: {value}")

    def print_ignored_files_warning(
        self,
        ignored_files: list[str],
        target_path: Path | None = None,
        will_force_add: bool = False,
    ) -> None:
        """Print warning about synced files that are gitignored by target project.

        This is the key UX improvement - showing users which files are silently
        ignored and providing commands to fix it.

        Args:
            ignored_files: List of gitignored file paths
            target_path: Target project path for git commands
            will_force_add: If True, files will be force-added automatically
                (user already specified --branch or --git-commit)
        """
        if not ignored_files:
            return

        # Import here to avoid circular dependency
        from persuade.commit_categorization import categorize_for_commits

        # Group files by category for cleaner output
        groups = categorize_for_commits(ignored_files)

        if RICH_AVAILABLE and self.console:
            self.console.print(
                "\n[bold yellow]⚠[/bold yellow] Synced files gitignored by your project:"
            )

            for group in groups:
                self.console.print(f"\n  [cyan]{group.message}[/cyan] ({len(group.files)} files):")
                for file_path in group.files[:5]:  # Show first 5
                    self.console.print(f"    [dim]{file_path}[/dim]")
                if len(group.files) > 5:
                    self.console.print(f"    [dim]... and {len(group.files) - 5} more[/dim]")

            if will_force_add:
                self.console.print("\n[dim]These files will be force-added automatically.[/dim]")
            else:
                self.console.print("\n[bold]To track these files, use one of these options:[/bold]")
                self.console.print(
                    "  1. Run [cyan]persuade sync --branch --git-commit[/cyan] "
                    "to auto-create commits"
                )
                self.console.print("  2. Manually run: [cyan]git add -f <files>[/cyan]")
        else:
            print("\n⚠ Synced files gitignored by your project:")

            for group in groups:
                print(f"\n  {group.message} ({len(group.files)} files):")
                for file_path in group.files[:5]:
                    print(f"    {file_path}")
                if len(group.files) > 5:
                    print(f"    ... and {len(group.files) - 5} more")

            if will_force_add:
                print("\nThese files will be force-added automatically.")
            else:
                print("\nTo track these files, use one of these options:")
                print("  1. Run: persuade sync --branch --git-commit")
                print("  2. Manually run: git add -f <files>")

    def print_progress(self, message: str, total: int | None = None) -> Any:
        """Show progress indicator."""
        if RICH_AVAILABLE and self.console:
            if total:
                return Progress(
                    SpinnerColumn(),
                    TextColumn("[progress.description]{task.description}"),
                    BarColumn(),
                    TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
                    console=self.console,
                )
            else:
                return self.console.status(message)
        else:
            print(message)
            return None


def show_help(target_path: Path, verbose: bool = False) -> None:
    """
    Display beautiful help output showing installed automation.

    Args:
        target_path: Path to target project
        verbose: Show detailed information
    """
    claude_dir = target_path / ".claude"

    if not claude_dir.exists():
        if RICH_AVAILABLE:
            console = Console()
            console.print(f"\n[yellow]No .claude/ directory found in {target_path}[/yellow]")
        else:
            print(f"\nNo .claude/ directory found in {target_path}")
        return

    if RICH_AVAILABLE:
        console = Console()
        console.print("\n[bold cyan]Claude Code Automation Status[/bold cyan]\n")
        console.print(f"[dim]Location:[/dim] {target_path}")
        console.print(f"[dim].claude/:[/dim] {claude_dir}\n")
    else:
        print("\nClaude Code Automation Status\n")
        print(f"Location: {target_path}")
        print(f".claude/: {claude_dir}\n")

    # Show commands
    commands_dir = claude_dir / "commands"
    if commands_dir.exists():
        command_files = list(commands_dir.glob("*.md"))

        if command_files:
            if RICH_AVAILABLE:
                console.print("[bold]Slash Commands:[/bold]\n")
            else:
                print("Slash Commands:\n")

            for cmd_file in command_files:
                cmd_name = cmd_file.stem
                # Read first line as description
                try:
                    with open(cmd_file) as f:
                        description = f.readline().strip()
                except (OSError, UnicodeDecodeError):
                    description = "No description"

                if RICH_AVAILABLE:
                    panel = Panel(
                        f"[dim]{description}[/dim]\n\n"
                        f"[dim]Usage:[/dim] /{cmd_name}\n"
                        f"[dim]File:[/dim] {cmd_file.relative_to(target_path)}",
                        title=f"/{cmd_name}",
                        border_style="cyan",
                    )
                    console.print(panel)
                else:
                    print(f"  /{cmd_name}")
                    print(f"    {description}")
                    print(f"    File: {cmd_file.relative_to(target_path)}")
                    print()

    # Show hooks
    settings_path = claude_dir / "settings.json"
    if settings_path.exists():
        try:
            with open(settings_path) as f:
                settings = json.load(f)

            hooks = settings.get("hooks", {})
            if hooks:
                if RICH_AVAILABLE:
                    console.print("\n[bold]Hooks:[/bold]\n")

                    table = Table(show_header=True)
                    table.add_column("Event", style="cyan")
                    table.add_column("Matcher", style="yellow")
                    table.add_column("Action", style="green")

                    for event, entries in hooks.items():
                        for entry in entries:
                            matcher = entry.get("matcher", "N/A")
                            action_hooks = entry.get("hooks", [])
                            action = (
                                action_hooks[0].get("command", "N/A") if action_hooks else "N/A"
                            )
                            table.add_row(event, matcher, action)

                    console.print(table)
                else:
                    print("\nHooks:\n")
                    for event, entries in hooks.items():
                        for entry in entries:
                            matcher = entry.get("matcher", "N/A")
                            print(f"  {event}: {matcher}")
        except (json.JSONDecodeError, KeyError):
            pass

    # Show scripts
    scripts_dir = claude_dir / "scripts"
    if scripts_dir.exists():
        script_files = [f for f in scripts_dir.glob("*.sh") if f.is_file()]

        if script_files:
            if RICH_AVAILABLE:
                console.print("\n[bold]Scripts:[/bold]\n")
            else:
                print("\nScripts:\n")

            for script_file in script_files:
                if RICH_AVAILABLE:
                    console.print(f"  [green]•[/green] {script_file.name}")
                else:
                    print(f"  • {script_file.name}")

    # Show permissions summary
    if settings_path.exists():
        try:
            with open(settings_path) as f:
                settings = json.load(f)

            allow_count = len(settings.get("permissions", {}).get("allow", []))
            deny_count = len(settings.get("permissions", {}).get("deny", []))

            if RICH_AVAILABLE:
                console.print("\n[bold]Permissions Summary:[/bold]")
                console.print(f"  Allow: {allow_count}")
                console.print(f"  Deny: {deny_count}\n")
            else:
                print("\nPermissions Summary:")
                print(f"  Allow: {allow_count}")
                print(f"  Deny: {deny_count}\n")
        except (json.JSONDecodeError, KeyError):
            pass
