#!/usr/bin/env python3
"""
ThrillWiki GitHub PAT Setup Helper
Interactive script for setting up GitHub Personal Access Tokens with proper validation
and integration with the automation system.

Features:
- Guided GitHub PAT creation process
- Token validation and permission checking
- Integration with existing github-auth.py patterns
- Clear instructions for PAT scope requirements
- Secure token storage with proper file permissions
"""

import sys
import getpass
import requests
import argparse
import subprocess
from pathlib import Path

# Configuration
SCRIPT_DIR = Path(__file__).parent
PROJECT_DIR = SCRIPT_DIR.parent.parent
CONFIG_SCRIPT = SCRIPT_DIR / "automation-config.sh"
GITHUB_AUTH_SCRIPT = PROJECT_DIR / "scripts" / "github-auth.py"
TOKEN_FILE = PROJECT_DIR / ".github-pat"

# GitHub API Configuration
GITHUB_API_BASE = "https://api.github.com"
REQUEST_TIMEOUT = 30

# Token scope requirements for different use cases
TOKEN_SCOPES = {
    "public": {
        "description": "Public repositories only",
        "scopes": ["public_repo"],
        "note": "Suitable for public repositories and basic automation",
    },
    "private": {
        "description": "Private repositories access",
        "scopes": ["repo"],
        "note": "Required for private repositories and full automation features",
    },
    "full": {
        "description": "Full automation capabilities",
        "scopes": ["repo", "workflow", "read:org"],
        "note": "Recommended for complete automation setup with GitHub Actions",
    },
}


class Colors:
    """ANSI color codes for terminal output"""

    RED = "\033[0;31m"
    GREEN = "\033[0;32m"
    YELLOW = "\033[1;33m"
    BLUE = "\033[0;34m"
    PURPLE = "\033[0;35m"
    CYAN = "\033[0;36m"
    BOLD = "\033[1m"
    NC = "\033[0m"  # No Color


def print_colored(message, color=Colors.NC):
    """Print colored message to terminal"""
    print(f"{color}{message}{Colors.NC}")


def print_error(message):
    """Print error message"""
    print_colored(f"❌ Error: {message}", Colors.RED)


def print_success(message):
    """Print success message"""
    print_colored(f"✅ {message}", Colors.GREEN)


def print_warning(message):
    """Print warning message"""
    print_colored(f"⚠️  Warning: {message}", Colors.YELLOW)


def print_info(message):
    """Print info message"""
    print_colored(f"ℹ️  {message}", Colors.BLUE)


def print_step(step, total, message):
    """Print step progress"""
    print_colored(f"\n[{step}/{total}] {message}", Colors.CYAN)


def validate_token_format(token):
    """Validate GitHub token format"""
    if not token:
        return False

    # GitHub token patterns
    patterns = [
        lambda t: t.startswith("ghp_") and len(t) >= 40,  # Classic PAT
        lambda t: t.startswith("github_pat_") and len(t) >= 50,  # Fine-grained PAT
        lambda t: t.startswith("gho_") and len(t) >= 40,  # OAuth token
        lambda t: t.startswith("ghu_") and len(t) >= 40,  # User token
        lambda t: t.startswith("ghs_") and len(t) >= 40,  # Server token
    ]

    return any(pattern(token) for pattern in patterns)


def test_github_token(token, timeout=REQUEST_TIMEOUT):
    """Test GitHub token by making API call"""
    if not token:
        return False, "No token provided"

    try:
        headers = {
            "Authorization": f"Bearer {token}",
            "Accept": "application/vnd.github+json",
            "X-GitHub-Api-Version": "2022-11-28",
        }

        response = requests.get(
            f"{GITHUB_API_BASE}/user", headers=headers, timeout=timeout
        )

        if response.status_code == 200:
            user_data = response.json()
            return (
                True,
                f"Valid token for user: {
                    user_data.get(
                        'login', 'unknown')}",
            )
        elif response.status_code == 401:
            return False, "Invalid or expired token"
        elif response.status_code == 403:
            return False, "Token lacks required permissions"
        else:
            return (
                False,
                f"API request failed with HTTP {
                    response.status_code}",
            )

    except requests.exceptions.RequestException as e:
        return False, f"Network error: {str(e)}"


def get_token_permissions(token, timeout=REQUEST_TIMEOUT):
    """Get token permissions and scopes"""
    if not token:
        return None, "No token provided"

    try:
        headers = {
            "Authorization": f"Bearer {token}",
            "Accept": "application/vnd.github+json",
            "X-GitHub-Api-Version": "2022-11-28",
        }

        # Get user info and check token in response headers
        response = requests.get(
            f"{GITHUB_API_BASE}/user", headers=headers, timeout=timeout
        )

        if response.status_code == 200:
            scopes = response.headers.get("X-OAuth-Scopes", "").split(", ")
            scopes = [scope.strip() for scope in scopes if scope.strip()]

            return scopes, None
        else:
            return (
                None,
                f"Failed to get permissions: HTTP {
                    response.status_code}",
            )

    except requests.exceptions.RequestException as e:
        return None, f"Network error: {str(e)}"


def check_repository_access(token, repo_url=None, timeout=REQUEST_TIMEOUT):
    """Check if token can access the repository"""
    if not token:
        return False, "No token provided"

    # Try to determine repository from git remote
    if not repo_url:
        try:
            result = subprocess.run(
                ["git", "remote", "get-url", "origin"],
                cwd=PROJECT_DIR,
                capture_output=True,
                text=True,
                timeout=10,
            )
            if result.returncode == 0:
                repo_url = result.stdout.strip()
        except (subprocess.TimeoutExpired, FileNotFoundError):
            pass

    if not repo_url:
        return None, "Could not determine repository URL"

    # Extract owner/repo from URL
    if "github.com" in repo_url:
        # Handle both SSH and HTTPS URLs
        if repo_url.startswith("git@github.com:"):
            repo_path = repo_url.replace("git@github.com:", "").replace(".git", "")
        elif "github.com/" in repo_url:
            repo_path = repo_url.split("github.com/")[-1].replace(".git", "")
        else:
            return None, "Could not parse repository URL"

        try:
            headers = {
                "Authorization": f"Bearer {token}",
                "Accept": "application/vnd.github+json",
                "X-GitHub-Api-Version": "2022-11-28",
            }

            response = requests.get(
                f"{GITHUB_API_BASE}/repos/{repo_path}",
                headers=headers,
                timeout=timeout,
            )

            if response.status_code == 200:
                repo_data = response.json()
                return (
                    True,
                    f"Access confirmed for {
                        repo_data.get(
                            'full_name', repo_path)}",
                )
            elif response.status_code == 404:
                return False, "Repository not found or no access"
            elif response.status_code == 403:
                return False, "Access denied - insufficient permissions"
            else:
                return (
                    False,
                    f"Access check failed: HTTP {
                        response.status_code}",
                )

        except requests.exceptions.RequestException as e:
            return None, f"Network error: {str(e)}"

    return None, "Not a GitHub repository"


def show_pat_instructions():
    """Show detailed PAT creation instructions"""
    print_colored("\n" + "=" * 60, Colors.BOLD)
    print_colored("GitHub Personal Access Token (PAT) Setup Guide", Colors.BOLD)
    print_colored("=" * 60, Colors.BOLD)

    print("\n🔐 Why do you need a GitHub PAT?")
    print("   • Access private repositories")
    print("   • Avoid GitHub API rate limits")
    print("   • Enable automated repository operations")
    print("   • Secure authentication without passwords")

    print("\n📋 Step-by-step PAT creation:")
    print("   1. Go to: https://github.com/settings/tokens")
    print("   2. Click 'Generate new token' → 'Generate new token (classic)'")
    print("   3. Enter a descriptive note: 'ThrillWiki Automation'")
    print("   4. Set expiration (recommended: 90 days for security)")
    print("   5. Select appropriate scopes:")

    print("\n🎯 Recommended scope configurations:")
    for scope_type, config in TOKEN_SCOPES.items():
        print(f"\n   {scope_type.upper()} REPOSITORIES:")
        print(f"   • Description: {config['description']}")
        print(f"   • Required scopes: {', '.join(config['scopes'])}")
        print(f"   • Note: {config['note']}")

    print("\n⚡ Quick setup for most users:")
    print("   • Select 'repo' scope for full repository access")
    print("   • This enables all automation features")

    print("\n🔒 Security best practices:")
    print("   • Use descriptive token names")
    print("   • Set reasonable expiration dates")
    print("   • Regenerate tokens regularly")
    print("   • Never share tokens in public")
    print("   • Delete unused tokens immediately")

    print("\n📱 After creating your token:")
    print("   • Copy the token immediately (it won't be shown again)")
    print("   • Return to this script and paste it when prompted")
    print("   • The script will validate and securely store your token")


def interactive_token_setup():
    """Interactive token setup process"""
    print_colored("\n🚀 ThrillWiki GitHub PAT Setup", Colors.BOLD)
    print_colored("================================", Colors.BOLD)

    # Check if token already exists
    if TOKEN_FILE.exists():
        try:
            existing_token = TOKEN_FILE.read_text().strip()
            if existing_token:
                print_info("Existing GitHub token found")

                # Test existing token
                valid, message = test_github_token(existing_token)
                if valid:
                    print_success(f"Current token is valid: {message}")

                    choice = (
                        input("\nDo you want to replace the existing token? (y/N): ")
                        .strip()
                        .lower()
                    )
                    if choice not in ["y", "yes"]:
                        print_info("Keeping existing token")
                        return True
                else:
                    print_warning(f"Current token is invalid: {message}")
                    print_info("Setting up new token...")
        except Exception as e:
            print_warning(f"Could not read existing token: {e}")

    # Show instructions
    print("\n" + "=" * 50)
    choice = (
        input("Do you want to see PAT creation instructions? (Y/n): ").strip().lower()
    )
    if choice not in ["n", "no"]:
        show_pat_instructions()

    # Get token from user
    print_step(1, 3, "Enter your GitHub Personal Access Token")
    print("📋 Please paste your GitHub PAT below:")
    print("   (Input will be hidden for security)")

    while True:
        try:
            token = getpass.getpass("GitHub PAT: ").strip()

            if not token:
                print_error("No token entered. Please try again.")
                continue

            # Validate format
            if not validate_token_format(token):
                print_error(
                    "Invalid token format. GitHub tokens should start with 'ghp_', 'github_pat_', etc."
                )
                retry = input("Try again? (Y/n): ").strip().lower()
                if retry in ["n", "no"]:
                    return False
                continue

            break

        except KeyboardInterrupt:
            print("\nSetup cancelled by user")
            return False

    # Test token
    print_step(2, 3, "Validating GitHub token")
    print("🔍 Testing token with GitHub API...")

    valid, message = test_github_token(token)
    if not valid:
        print_error(f"Token validation failed: {message}")
        return False

    print_success(message)

    # Check permissions
    print("🔐 Checking token permissions...")
    scopes, error = get_token_permissions(token)
    if error:
        print_warning(f"Could not check permissions: {error}")
    else:
        print_success(
            f"Token scopes: {', '.join(scopes) if scopes else 'None detected'}"
        )

        # Check for recommended scopes
        has_repo = "repo" in scopes or "public_repo" in scopes
        if not has_repo:
            print_warning("Token may lack repository access permissions")

    # Check repository access
    print("📁 Checking repository access...")
    access, access_message = check_repository_access(token)
    if access is True:
        print_success(access_message)
    elif access is False:
        print_warning(access_message)
    else:
        print_info(access_message or "Repository access check skipped")

    # Store token
    print_step(3, 3, "Storing GitHub token securely")

    try:
        # Backup existing token if it exists
        if TOKEN_FILE.exists():
            backup_file = TOKEN_FILE.with_suffix(".backup")
            TOKEN_FILE.rename(backup_file)
            print_info(f"Existing token backed up to: {backup_file}")

        # Write new token
        TOKEN_FILE.write_text(token)
        TOKEN_FILE.chmod(0o600)  # Read/write for owner only

        print_success(f"Token stored securely in: {TOKEN_FILE}")

        # Try to update configuration via config script
        try:
            if CONFIG_SCRIPT.exists():
                subprocess.run(
                    [
                        "bash",
                        "-c",
                        f'source {CONFIG_SCRIPT} && store_github_token "{token}"',
                    ],
                    check=False,
                    capture_output=True,
                )
                print_success("Token added to automation configuration")
        except Exception as e:
            print_warning(f"Could not update automation config: {e}")

        print_success("GitHub PAT setup completed successfully!")
        return True

    except Exception as e:
        print_error(f"Failed to store token: {e}")
        return False


def validate_existing_token():
    """Validate existing GitHub token"""
    print_colored("\n🔍 GitHub Token Validation", Colors.BOLD)
    print_colored("===========================", Colors.BOLD)

    if not TOKEN_FILE.exists():
        print_error("No GitHub token file found")
        print_info(f"Expected location: {TOKEN_FILE}")
        return False

    try:
        token = TOKEN_FILE.read_text().strip()
        if not token:
            print_error("Token file is empty")
            return False

        print_info("Validating stored token...")

        # Format validation
        if not validate_token_format(token):
            print_error("Token format is invalid")
            return False

        print_success("Token format is valid")

        # API validation
        valid, message = test_github_token(token)
        if not valid:
            print_error(f"Token validation failed: {message}")
            return False

        print_success(message)

        # Check permissions
        scopes, error = get_token_permissions(token)
        if error:
            print_warning(f"Could not check permissions: {error}")
        else:
            print_success(
                f"Token scopes: {
                    ', '.join(scopes) if scopes else 'None detected'}"
            )

        # Check repository access
        access, access_message = check_repository_access(token)
        if access is True:
            print_success(access_message)
        elif access is False:
            print_warning(access_message)
        else:
            print_info(access_message or "Repository access check inconclusive")

        print_success("Token validation completed")
        return True

    except Exception as e:
        print_error(f"Error reading token: {e}")
        return False


def remove_token():
    """Remove stored GitHub token"""
    print_colored("\n🗑️  GitHub Token Removal", Colors.BOLD)
    print_colored("=========================", Colors.BOLD)

    if not TOKEN_FILE.exists():
        print_info("No GitHub token file found")
        return True

    try:
        # Backup before removal
        backup_file = TOKEN_FILE.with_suffix(".removed")
        TOKEN_FILE.rename(backup_file)
        print_success(f"Token removed and backed up to: {backup_file}")

        # Try to remove from config
        try:
            if CONFIG_SCRIPT.exists():
                subprocess.run(
                    [
                        "bash",
                        "-c",
                        f"source {CONFIG_SCRIPT} && remove_github_token",
                    ],
                    check=False,
                    capture_output=True,
                )
                print_success("Token removed from automation configuration")
        except Exception as e:
            print_warning(f"Could not update automation config: {e}")

        print_success("GitHub token removed successfully")
        return True

    except Exception as e:
        print_error(f"Error removing token: {e}")
        return False


def show_token_status():
    """Show current token status"""
    print_colored("\n📊 GitHub Token Status", Colors.BOLD)
    print_colored("======================", Colors.BOLD)

    # Check token file
    print(f"📁 Token file: {TOKEN_FILE}")
    if TOKEN_FILE.exists():
        print_success("Token file exists")

        # Check permissions
        perms = oct(TOKEN_FILE.stat().st_mode)[-3:]
        if perms == "600":
            print_success(f"File permissions: {perms} (secure)")
        else:
            print_warning(f"File permissions: {perms} (should be 600)")

        # Quick validation
        try:
            token = TOKEN_FILE.read_text().strip()
            if token:
                if validate_token_format(token):
                    print_success("Token format is valid")

                    # Quick API test
                    valid, message = test_github_token(token, timeout=10)
                    if valid:
                        print_success(f"Token is valid: {message}")
                    else:
                        print_error(f"Token is invalid: {message}")
                else:
                    print_error("Token format is invalid")
            else:
                print_error("Token file is empty")
        except Exception as e:
            print_error(f"Error reading token: {e}")
    else:
        print_warning("Token file not found")

    # Check config integration
    print(f"\n⚙️  Configuration: {CONFIG_SCRIPT}")
    if CONFIG_SCRIPT.exists():
        print_success("Configuration script available")
    else:
        print_warning("Configuration script not found")

    # Check existing GitHub auth script
    print(f"\n🔐 GitHub auth script: {GITHUB_AUTH_SCRIPT}")
    if GITHUB_AUTH_SCRIPT.exists():
        print_success("GitHub auth script available")
    else:
        print_warning("GitHub auth script not found")


def main():
    """Main CLI interface"""
    parser = argparse.ArgumentParser(
        description="ThrillWiki GitHub PAT Setup Helper",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  %(prog)s setup                    # Interactive token setup
  %(prog)s validate                 # Validate existing token
  %(prog)s status                   # Show token status
  %(prog)s remove                   # Remove stored token
  %(prog)s --help                   # Show this help

For detailed PAT creation instructions, run: %(prog)s setup
        """,
    )

    parser.add_argument(
        "command",
        choices=["setup", "validate", "status", "remove", "help"],
        help="Command to execute",
    )

    parser.add_argument(
        "--token", help="GitHub token to validate (for validate command)"
    )

    parser.add_argument(
        "--force", action="store_true", help="Force operation without prompts"
    )

    if len(sys.argv) == 1:
        parser.print_help()
        sys.exit(1)

    args = parser.parse_args()

    try:
        if args.command == "setup":
            success = interactive_token_setup()
            sys.exit(0 if success else 1)

        elif args.command == "validate":
            if args.token:
                # Validate provided token
                print_info("Validating provided token...")
                if validate_token_format(args.token):
                    valid, message = test_github_token(args.token)
                    if valid:
                        print_success(message)
                        sys.exit(0)
                    else:
                        print_error(message)
                        sys.exit(1)
                else:
                    print_error("Invalid token format")
                    sys.exit(1)
            else:
                # Validate existing token
                success = validate_existing_token()
                sys.exit(0 if success else 1)

        elif args.command == "status":
            show_token_status()
            sys.exit(0)

        elif args.command == "remove":
            if not args.force:
                confirm = (
                    input("Are you sure you want to remove the GitHub token? (y/N): ")
                    .strip()
                    .lower()
                )
                if confirm not in ["y", "yes"]:
                    print_info("Operation cancelled")
                    sys.exit(0)

            success = remove_token()
            sys.exit(0 if success else 1)

        elif args.command == "help":
            parser.print_help()
            sys.exit(0)

    except KeyboardInterrupt:
        print("\nOperation cancelled by user")
        sys.exit(1)
    except Exception as e:
        print_error(f"Unexpected error: {e}")
        sys.exit(1)


if __name__ == "__main__":
    main()
