"""Git hooks integration for prefact.

This module provides utilities for installing and managing Git hooks
that run prefact scans before commits and other Git operations.
"""

from __future__ import annotations

import os
import stat
import subprocess
from pathlib import Path
from typing import Dict, List, Optional

from prefact.config import Config
from prefact.engine import RefactoringEngine


class GitHooks:
    """Manages Git hooks for prefact."""
    
    def __init__(self, repo_root: Path, config: Optional[Config] = None):
        self.repo_root = repo_root.resolve()
        self.git_dir = self._find_git_dir()
        self.hooks_dir = self.git_dir / "hooks"
        self.config = config or Config(project_root=self.repo_root)
    
    def _find_git_dir(self) -> Path:
        """Find the .git directory."""
        git_dir = self.repo_root / ".git"
        if git_dir.is_file():
            # Git worktree
            with open(git_dir) as f:
                git_dir = self.repo_root / f.read().strip().split(" ")[1]
        return git_dir
    
    def install_hooks(self, hook_types: Optional[List[str]] = None) -> None:
        """Install Git hooks for prefact."""
        if hook_types is None:
            hook_types = ["pre-commit", "pre-push", "commit-msg"]
        
        # Ensure hooks directory exists
        self.hooks_dir.mkdir(exist_ok=True)
        
        for hook_type in hook_types:
            hook_path = self.hooks_dir / hook_type
            self._install_hook(hook_type, hook_path)
    
    def _install_hook(self, hook_type: str, hook_path: Path) -> None:
        """Install a specific Git hook."""
        hook_content = self._generate_hook_script(hook_type)
        
        # Backup existing hook if it exists
        if hook_path.exists():
            backup_path = hook_path.with_suffix(".prefact.bak")
            hook_path.rename(backup_path)
            print(f"Backed up existing hook to {backup_path}")
        
        # Write new hook
        hook_path.write_text(hook_content)
        
        # Make hook executable
        current_permissions = hook_path.stat().st_mode
        hook_path.chmod(current_permissions | stat.S_IEXEC)
        
        print(f"Installed {hook_type} hook")
    
    def _generate_hook_script(self, hook_type: str) -> str:
        """Generate the script content for a hook."""
        if hook_type == "pre-commit":
            return self._pre_commit_hook()
        elif hook_type == "pre-push":
            return self._pre_push_hook()
        elif hook_type == "commit-msg":
            return self._commit_msg_hook()
        else:
            raise ValueError(f"Unsupported hook type: {hook_type}")
    
    def _pre_commit_hook(self) -> str:
        """Generate pre-commit hook script."""
        return f"""#!/bin/bash
# Pprefact pre-commit hook
# Runs prefact on staged files before commit

set -e

# Get list of staged Python files
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep '\\.py$' || true)

if [ -z "$STAGED_FILES" ]; then
    echo "No Python files staged, skipping prefact check"
    exit 0
fi

echo "Running prefact on staged Python files..."
echo "Files: $STAGED_FILES"

# Run prefact scan
if command -v prefact &> /dev/null; then
    # Use installed prefact
    echo "$STAGED_FILES" | xargs prefact scan --path -
else
    # Use python -m prefact
    echo "$STAGED_FILES" | xargs python -m prefact scan --path -
fi

# Check if any issues were found
if [ $? -ne 0 ]; then
    echo ""
    echo "prefact found issues in staged files."
    echo "Fix them before committing, or use --no-verify to bypass."
    echo ""
    echo "To fix automatically:"
    echo "  prefact fix --path <file>"
    echo ""
    exit 1
fi

echo "prefact check passed"
exit 0
"""
    
    def _pre_push_hook(self) -> str:
        """Generate pre-push hook script."""
        return f"""#!/bin/bash
# Pprefact pre-push hook
# Runs prefact on all files before pushing

set -e

echo "Running prefact check before push..."

# Run prefact on the entire project
if command -v prefact &> /dev/null; then
    prefact scan --path {self.repo_root}
else
    python -m prefact scan --path {self.repo_root}
fi

# Check if any issues were found
if [ $? -ne 0 ]; then
    echo ""
    echo "prefact found issues in the repository."
    echo "Fix them before pushing, or use --no-verify to bypass."
    echo ""
    exit 1
fi

echo "prefact check passed"
exit 0
"""
    
    def _commit_msg_hook(self) -> str:
        """Generate commit-msg hook script."""
        return f"""#!/bin/bash
# Pprefact commit-msg hook
# Validates commit messages

set -e

commit_msg_file=$1

# Read commit message
commit_msg=$(cat "$commit_msg_file")

# Check for common issues
if [[ "$commit_msg" =~ ^[Ww][Ii][Pp] ]]; then
    echo "Commit message starts with 'WIP' - aborting commit"
    echo "Remove 'WIP' prefix or use --no-verify to bypass"
    exit 1
fi

# Check minimum length
if [ ${{#commit_msg}} -lt 10 ]; then
    echo "Commit message too short (minimum 10 characters)"
    echo "Provide a more descriptive message or use --no-verify to bypass"
    exit 1
fi

exit 0
"""
    
    def uninstall_hooks(self, hook_types: Optional[List[str]] = None) -> None:
        """Uninstall Git hooks."""
        if hook_types is None:
            hook_types = ["pre-commit", "pre-push", "commit-msg"]
        
        for hook_type in hook_types:
            hook_path = self.hooks_dir / hook_type
            backup_path = hook_path.with_suffix(".prefact.bak")
            
            if hook_path.exists():
                # Check if it's a prefact hook
                content = hook_path.read_text()
                if "prefact" in content:
                    hook_path.unlink()
                    
                    # Restore backup if it exists
                    if backup_path.exists():
                        backup_path.rename(hook_path)
                        print(f"Restored original {hook_type} hook")
                    else:
                        print(f"Removed {hook_type} hook")
    
    def list_hooks(self) -> Dict[str, bool]:
        """List status of all hooks."""
        hook_types = ["pre-commit", "pre-push", "commit-msg"]
        status = {}
        
        for hook_type in hook_types:
            hook_path = self.hooks_dir / hook_type
            if hook_path.exists():
                content = hook_path.read_text()
                status[hook_type] = "prefact" in content
            else:
                status[hook_type] = False
        
        return status
    
    def test_hook(self, hook_type: str) -> bool:
        """Test if a hook is working correctly."""
        hook_path = self.hooks_dir / hook_type
        
        if not hook_path.exists():
            return False
        
        try:
            # Run the hook with --help or similar to test
            result = subprocess.run(
                [str(hook_path)],
                capture_output=True,
                text=True,
                cwd=self.repo_root,
                timeout=5
            )
            # Hooks may return non-zero for testing, that's OK
            return True
        except Exception:
            return False


class PreCommitConfig:
    """Generate pre-commit configuration for prefact."""
    
    @staticmethod
    def generate_config(repo_root: Path) -> str:
        """Generate .pre-commit-config.yaml content."""
        return f"""# Pre-commit configuration for prefact
# See https://pre-commit.com for more information

repos:
  - repo: local
    hooks:
      - id: prefact-scan
        name: prefact scan
        entry: prefact
        language: system
        args: [scan, --path, .]
        types: [python]
        pass_filenames: false
        always_run: false
        
      - id: prefact-fix
        name: prefact fix
        entry: prefact
        language: system
        args: [fix, --path, .]
        types: [python]
        pass_filenames: false
        always_run: false
        
  # Additional hooks that work well with prefact
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.5.6
    hooks:
      - id: ruff
        args: [--fix]
      - id: ruff-format
        
  - repo: https://github.com/psf/black
    rev: 24.4.2
    hooks:
      - id: black
        
  - repo: https://github.com/pycqa/isort
    rev: 5.13.2
    hooks:
      - id: isort
        args: ["--profile", "black"]
"""
    
    @staticmethod
    def install(repo_root: Path) -> None:
        """Install pre-commit configuration."""
        config_path = repo_root / ".pre-commit-config.yaml"
        
        if config_path.exists():
            backup_path = config_path.with_suffix(".prefact.bak")
            config_path.rename(backup_path)
            print(f"Backed up existing config to {backup_path}")
        
        config_path.write_text(PreCommitConfig.generate_config(repo_root))
        print(f"Installed pre-commit config at {config_path}")
        
        # Install pre-commit hooks
        try:
            subprocess.run(
                ["pre-commit", "install"],
                cwd=repo_root,
                check=True,
                capture_output=True
            )
            print("Installed pre-commit hooks")
        except subprocess.CalledProcessError as e:
            print(f"Failed to install pre-commit hooks: {e}")
            print("Install pre-commit with: pip install pre-commit")


def install_git_hooks(repo_root: Optional[Path] = None) -> None:
    """Install Git hooks for the current repository."""
    if repo_root is None:
        repo_root = Path.cwd()
    
    # Check if we're in a Git repository
    git_dir = repo_root / ".git"
    if not git_dir.exists() and not git_dir.is_file():
        print("Error: Not in a Git repository")
        return
    
    # Install hooks
    hooks = GitHooks(repo_root)
    hooks.install_hooks()
    
    print("\nGit hooks installed successfully!")
    print("They will run before each commit/push.")
    print("To bypass, use: git commit --no-verify")


def uninstall_git_hooks(repo_root: Optional[Path] = None) -> None:
    """Uninstall Git hooks for the current repository."""
    if repo_root is None:
        repo_root = Path.cwd()
    
    hooks = GitHooks(repo_root)
    hooks.uninstall_hooks()
    
    print("Git hooks uninstalled")


def list_git_hooks(repo_root: Optional[Path] = None) -> None:
    """List status of Git hooks."""
    if repo_root is None:
        repo_root = Path.cwd()
    
    hooks = GitHooks(repo_root)
    status = hooks.list_hooks()
    
    print("Git hooks status:")
    for hook_type, installed in status.items():
        status_str = "✓ Installed (prefact)" if installed else "✗ Not installed"
        print(f"  {hook_type}: {status_str}")


# CLI commands
def main():
    """Main CLI for Git hooks management."""
    import argparse
    
    parser = argparse.ArgumentParser(description="Manage Git hooks for prefact")
    parser.add_argument("command", choices=["install", "uninstall", "list", "test"])
    parser.add_argument("--path", type=Path, default=Path.cwd(), help="Repository path")
    parser.add_argument("--hooks", nargs="+", help="Specific hooks to manage")
    
    args = parser.parse_args()
    
    if args.command == "install":
        install_git_hooks(args.path)
    elif args.command == "uninstall":
        uninstall_git_hooks(args.path)
    elif args.command == "list":
        list_git_hooks(args.path)
    elif args.command == "test":
        hooks = GitHooks(args.path)
        for hook_type in args.hooks or ["pre-commit", "pre-push", "commit-msg"]:
            result = hooks.test_hook(hook_type)
            status = "✓ Working" if result else "✗ Not working"
            print(f"{hook_type}: {status}")


if __name__ == "__main__":
    main()
