"""Main entry point for Vibe Package Manager CLI."""

import argparse
import sys
from pathlib import Path
from typing import Dict, List

from .config_parser import VibePackageConfig, find_vibe_package_config
from .skill_sync import SkillSync
from .subagent_sync import SubAgentSync
from .mcp_installer import MCPInstaller
from .base_sync import VIBE_PACKAGE_DIR
from . import __version__

# Global debug mode flag
DEBUG_MODE = False


class PackageTracker:
    """Tracks loaded packages to avoid duplicates and circular dependencies."""

    _VALID_TYPES = {"configs", "skills", "subagents", "mcps"}

    def __init__(self):
        self._loaded: Dict[str, set] = {t: set() for t in self._VALID_TYPES}
        self.config_stack: List[str] = []

    def is_loaded(self, item_type: str, name: str) -> bool:
        """Check if an item of given type has been loaded."""
        if item_type not in self._VALID_TYPES:
            raise ValueError(f"Invalid item type: {item_type}")
        return str(name) in self._loaded[item_type]

    def mark_loaded(self, item_type: str, name: str) -> None:
        """Mark an item of given type as loaded."""
        if item_type not in self._VALID_TYPES:
            raise ValueError(f"Invalid item type: {item_type}")
        self._loaded[item_type].add(str(name))

    # Convenience methods for backward compatibility
    def is_config_loaded(self, config_path: str) -> bool:
        """Check if a config file has been loaded."""
        return self.is_loaded("configs", config_path)

    def mark_config_loaded(self, config_path: str) -> None:
        """Mark a config file as loaded."""
        self.mark_loaded("configs", config_path)

    def is_skill_loaded(self, skill_name: str) -> bool:
        """Check if a skill has been loaded."""
        return self.is_loaded("skills", skill_name)

    def mark_skill_loaded(self, skill_name: str) -> None:
        """Mark a skill as loaded."""
        self.mark_loaded("skills", skill_name)

    def is_subagent_loaded(self, subagent_name: str) -> bool:
        """Check if a subagent has been loaded."""
        return self.is_loaded("subagents", subagent_name)

    def mark_subagent_loaded(self, subagent_name: str) -> None:
        """Mark a subagent as loaded."""
        self.mark_loaded("subagents", subagent_name)

    def is_mcp_loaded(self, mcp_name: str) -> bool:
        """Check if an MCP has been loaded."""
        return self.is_loaded("mcps", mcp_name)

    def mark_mcp_loaded(self, mcp_name: str) -> None:
        """Mark an MCP as loaded."""
        self.mark_loaded("mcps", mcp_name)

    def push_config(self, config_path: str) -> None:
        """Push config path onto stack."""
        self.config_stack.append(str(config_path))

    def pop_config(self) -> None:
        """Pop config path from stack."""
        if self.config_stack:
            self.config_stack.pop()

    def check_circular(self, config_path: str) -> bool:
        """Check if loading this config would create a circular dependency."""
        return str(config_path) in self.config_stack


def debug_log(message: str) -> None:
    """Print debug message if debug mode is enabled."""
    if DEBUG_MODE:
        print(f"[DEBUG] {message}")


def get_current_versions() -> dict:
    """
    Get current versions of supported agents.

    Returns:
        Dictionary mapping agent names to their versions
    """
    # TODO: Implement version detection logic
    # For now, return placeholder versions
    return {
        'iflow': '0.5.17',
        'claude': '2.1.79-20260319.1'
    }


def detect_local_agents(base_dir: Path, config_agents: List[str], config: VibePackageConfig) -> List[str]:
    """
    Detect locally installed agents by checking if their CLI commands are available.

    Args:
        base_dir: Base directory for operations
        config_agents: List of configured target agents from vibe_package.json
        config: VibePackageConfig instance for agent command resolution

    Returns:
        List of detected agent names that have available CLI commands
    """
    import shutil

    local_agents = []

    for agent in config_agents:
        cli_command = config.get_agent_cli_command(agent)

        # Check if command exists in PATH
        command_exists = shutil.which(cli_command) is not None

        if command_exists:
            debug_log(f"Detected local agent '{agent}' (CLI: {cli_command})")
            local_agents.append(agent)
        else:
            debug_log(f"Agent '{agent}' (CLI: {cli_command}) not found in PATH")

    return local_agents


def install_command(args):
    """Handle the install command."""
    global DEBUG_MODE
    DEBUG_MODE = getattr(args, 'debug', False)

    debug_log("Starting install command")
    print("Starting Vibe Package Manager install...")

    # Find and load configuration
    try:
        debug_log(f"Finding configuration file (custom path: {args.config})")
        if args.config:
            # If custom path is provided, use it directly
            config_path = Path(args.config).resolve()
            if not config_path.exists():
                raise FileNotFoundError(f"Configuration file not found: {config_path}")
        else:
            # Otherwise, search from current directory
            config_path = find_vibe_package_config(args.config)
        debug_log(f"Configuration file found: {config_path}")
        print(f"Using configuration: {config_path}")
    except FileNotFoundError as e:
        debug_log(f"Configuration file not found: {e}")
        print(f"Error: {e}", file=sys.stderr)
        sys.exit(1)

    try:
        debug_log("Loading and validating configuration")
        config = VibePackageConfig(str(config_path))
        debug_log("Configuration loaded successfully")
    except ValueError as e:
        debug_log(f"Configuration validation failed: {e}")
        print(f"Configuration error: {e}", file=sys.stderr)
        sys.exit(1)

    base_dir = Path.cwd()
    config_target_agents = config.target_agents
    current_versions = get_current_versions()

    debug_log(f"Base directory: {base_dir}")
    debug_log(f"Configured target agents: {config_target_agents}")
    debug_log(f"Current versions: {current_versions}")

    # Detect locally installed agents by checking CLI commands
    local_agents = detect_local_agents(base_dir, config_target_agents, config)
    debug_log(f"Detected local agents: {local_agents}")

    # Calculate intersection of configured target_agents and local agents
    target_agents = [agent for agent in config_target_agents if agent in local_agents]

    if not target_agents:
        print(f"Warning: None of the configured target_agents ({', '.join(config_target_agents)}) are installed locally.")
        print(f"Detected local agents: {', '.join(local_agents) if local_agents else 'None'}")
        print("Nothing to install.")
        sys.exit(0)

    # Log which agents will be skipped
    skipped_agents = [agent for agent in config_target_agents if agent not in local_agents]
    if skipped_agents:
        print(f"Note: Skipping agents not installed locally: {', '.join(skipped_agents)}")

    print(f"Target agents: {', '.join(target_agents)}")

    # Create package tracker to avoid duplicates
    tracker = PackageTracker()
    tracker.mark_config_loaded(config_path)
    tracker.push_config(config_path)

    all_warnings = []

    # Sync SKILLs
    skills = config.get_skills()
    debug_log(f"Found {len(skills)} SKILL(s) to sync")
    if skills:
        print(f"\nSyncing {len(skills)} SKILL(s)...")
        skill_sync = SkillSync(str(base_dir), target_agents, current_versions, DEBUG_MODE, tracker, str(config_path))

        for skill_name, skill_config in skills.items():
            try:
                debug_log(f"Starting to sync SKILL: {skill_name}")
                print(f"\nProcessing SKILL: {skill_name}")
                warnings = skill_sync.sync_skill(skill_name, skill_config)
                debug_log(f"Successfully synced SKILL: {skill_name}")
                tracker.mark_skill_loaded(skill_name)
                all_warnings.extend(warnings)
            except Exception as e:
                debug_log(f"Error syncing SKILL '{skill_name}': {e}")
                print(f"Error syncing SKILL '{skill_name}': {e}", file=sys.stderr)
                if not args.continue_on_error:
                    skill_sync.cleanup_cache()
                    sys.exit(1)

        # Clean up skill cache after all skills are processed
        skill_sync.cleanup_cache()

    # Sync SubAgents
    subagents = config.get_subagents()
    debug_log(f"Found {len(subagents)} SubAgent(s) to sync")
    if subagents:
        print(f"\nSyncing {len(subagents)} SubAgent(s)...")
        subagent_sync = SubAgentSync(str(base_dir), target_agents, current_versions, DEBUG_MODE, tracker, str(config_path))

        for subagent_name, subagent_config in subagents.items():
            try:
                debug_log(f"Starting to sync SubAgent: {subagent_name}")
                print(f"\nProcessing SubAgent: {subagent_name}")
                warnings = subagent_sync.sync_subagent(subagent_name, subagent_config)
                debug_log(f"Successfully synced SubAgent: {subagent_name}")
                tracker.mark_subagent_loaded(subagent_name)
                all_warnings.extend(warnings)
            except Exception as e:
                debug_log(f"Error syncing SubAgent '{subagent_name}': {e}")
                print(f"Error syncing SubAgent '{subagent_name}': {e}", file=sys.stderr)
                if not args.continue_on_error:
                    sys.exit(1)

    # Install MCPs
    mcps = config.get_mcps()
    debug_log(f"Found {len(mcps)} MCP(s) to install")
    if mcps:
        print(f"\nInstalling {len(mcps)} MCP(s)...")
        mcp_installer = MCPInstaller(str(base_dir), target_agents, DEBUG_MODE, tracker, config)

        for mcp_name, mcp_config in mcps.items():
            try:
                debug_log(f"Starting to install MCP: {mcp_name}")
                print(f"\nProcessing MCP: {mcp_name}")
                mcp_installer.install_mcp(mcp_name, mcp_config)
                debug_log(f"Successfully installed MCP: {mcp_name}")
                tracker.mark_mcp_loaded(mcp_name)
            except Exception as e:
                debug_log(f"Error installing MCP '{mcp_name}': {e}")
                print(f"Error installing MCP '{mcp_name}': {e}", file=sys.stderr)
                if not args.continue_on_error:
                    sys.exit(1)

    # Print warnings
    if all_warnings:
        print("\n" + "="*60)
        print("WARNINGS:")
        print("="*60)
        for warning in all_warnings:
            print(f"  {warning}")

    debug_log("Install completed successfully")
    tracker.pop_config()
    print("\n" + "="*60)
    print("Install completed successfully!")
    print("="*60)

    # Clean up .vibe_package directory and backup files
    if not getattr(args, 'no_cleanup', False):
        vibe_package_dir = base_dir / VIBE_PACKAGE_DIR
        if vibe_package_dir.exists():
            debug_log(f"Cleaning up {VIBE_PACKAGE_DIR} directory: {vibe_package_dir}")
            import shutil
            import subprocess

            # First, clean up backup files
            try:
                backup_dirs = list(vibe_package_dir.glob('*_backup_*'))
                if backup_dirs:
                    debug_log(f"Found {len(backup_dirs)} backup directories to clean")
                    for backup_dir in backup_dirs:
                        debug_log(f"Removing backup: {backup_dir.name}")
                        try:
                            # Try git clean on backup directory
                            subprocess.run(
                                ['git', 'clean', '-fdx'],
                                cwd=backup_dir,
                                capture_output=True,
                                text=True,
                                timeout=10
                            )
                        except Exception:
                            pass  # Ignore git clean failures
                        try:
                            shutil.rmtree(backup_dir, ignore_errors=True)
                            debug_log(f"Removed backup: {backup_dir.name}")
                        except Exception as e:
                            debug_log(f"Failed to remove backup {backup_dir.name}: {e}")
            except Exception as e:
                debug_log(f"Error cleaning backup files: {e}")

            # Then clean up main directory
            try:
                # Try using git clean first to handle locked files
                subprocess.run(
                    ['git', 'clean', '-fdx'],
                    cwd=vibe_package_dir,
                    capture_output=True,
                    text=True,
                    timeout=30
                )
                debug_log("Git clean completed")
            except Exception as e:
                debug_log(f"Git clean failed: {e}, continuing with rmtree")

            # Clear read-only attributes before removal
            try:
                import stat
                for item in vibe_package_dir.rglob('*'):
                    if item.is_file():
                        try:
                            item.chmod(stat.S_IWRITE)
                        except (OSError, PermissionError):
                            pass
                debug_log("Cleared read-only attributes")
            except Exception as e:
                debug_log(f"Failed to clear attributes: {e}")

            try:
                shutil.rmtree(vibe_package_dir, ignore_errors=True)
                debug_log(f"{VIBE_PACKAGE_DIR} directory removed")
            except Exception as e:
                debug_log(f"Failed to remove {VIBE_PACKAGE_DIR} directory: {e}")
                print(f"Warning: Failed to remove {VIBE_PACKAGE_DIR} directory: {e}")
                print(f"You may need to manually delete: {vibe_package_dir}")
    else:
        debug_log("Skipping cleanup (--no-cleanup flag set)")
        print(f"Note: {VIBE_PACKAGE_DIR} directory was preserved (--no-cleanup)")


def validate_command(args):
    """Handle the validate command."""
    print("Validating vibe_package.json...")

    try:
        if args.config:
            # If custom path is provided, use it directly
            config_path = Path(args.config).resolve()
            if not config_path.exists():
                raise FileNotFoundError(f"Configuration file not found: {config_path}")
        else:
            # Otherwise, search from current directory
            config_path = find_vibe_package_config(args.config)
    except FileNotFoundError as e:
        print(f"Error: {e}", file=sys.stderr)
        sys.exit(1)

    try:
        config = VibePackageConfig(str(config_path))
        print(f"[OK] Configuration file is valid: {config_path}")
        print(f"  Target agents: {', '.join(config.target_agents)}")
        print(f"  SKILLs: {len(config.get_skills())}")
        print(f"  SubAgents: {len(config.get_subagents())}")
        print(f"  MCPs: {len(config.get_mcps())}")
    except ValueError as e:
        print(f"Configuration error: {e}", file=sys.stderr)
        sys.exit(1)


def main():
    """Main entry point."""
    parser = argparse.ArgumentParser(
        description='Vibe Package Manager - Manage SKILLs, SubAgents, and MCPs',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  %(prog)s install              Install all dependencies
  %(prog)s install -c path/to/vibe_package.json  Install with custom config
  %(prog)s validate             Validate configuration file
        """
    )

    # Add version argument
    parser.add_argument(
        '-v', '--version',
        action='version',
        version=f'%(prog)s {__version__}',
        help='Show version information and exit'
    )

    subparsers = parser.add_subparsers(dest='command', help='Available commands')

    # Install command
    install_parser = subparsers.add_parser('install', help='Install all dependencies')
    install_parser.add_argument(
        '-c', '--config',
        help='Path to vibe_package.json (default: searches from current directory)'
    )
    install_parser.add_argument(
        '--continue-on-error',
        action='store_true',
        help='Continue processing even if some dependencies fail'
    )
    install_parser.add_argument(
        '-d', '--debug',
        action='store_true',
        help='Enable debug mode for detailed logging'
    )
    install_parser.add_argument(
        '--no-cleanup',
        action='store_true',
        help=f'Do not remove {VIBE_PACKAGE_DIR} directory after install'
    )

    # Validate command
    validate_parser = subparsers.add_parser('validate', help='Validate configuration file')
    validate_parser.add_argument(
        '-c', '--config',
        help='Path to vibe_package.json (default: searches from current directory)'
    )

    args = parser.parse_args()

    if not args.command:
        parser.print_help()
        sys.exit(1)

    if args.command == 'install':
        install_command(args)
    elif args.command == 'validate':
        validate_command(args)
    else:
        parser.print_help()
        sys.exit(1)


if __name__ == '__main__':
    main()
