"""Commands for different tech stacks."""

import subprocess
import os
from pathlib import Path
from typing import List, Tuple, Optional
from .stack_detect import TechStack


class CommandRunner:
    """Runs install and test commands for different tech stacks."""
    
    def __init__(self, project_path: Path):
        """Initialize command runner with project path."""
        self.project_path = Path(project_path)
    
    def run_commands(self, stack: TechStack) -> bool:
        """Run install and test commands for the detected stack.
        
        Args:
            stack: The detected tech stack
            
        Returns:
            True if all commands succeeded, False otherwise
        """
        if stack == TechStack.LARAVEL:
            return self._run_laravel_commands()
        elif stack == TechStack.PHOENIX:
            return self._run_phoenix_commands()
        elif stack == TechStack.PYTHON:
            return self._run_python_commands()
        else:
            print(f"No commands defined for stack: {stack.value}")
            return False
    
    def _run_laravel_commands(self) -> bool:
        """Run Laravel install and test commands."""
        print("\n🔧 Running Laravel commands...")
        
        commands = [
            (["composer", "install"], "Installing PHP dependencies"),
            (["composer", "dump-autoload"], "Generating autoload files")
        ]
        
        # Add test command if test files exist
        test_commands = self._get_laravel_test_commands()
        commands.extend(test_commands)
        
        return self._execute_commands(commands)
    
    def _run_phoenix_commands(self) -> bool:
        """Run Phoenix install and test commands."""
        print("\n🔧 Running Phoenix commands...")
        
        commands = [
            (["mix", "deps.get"], "Installing Elixir dependencies"),
            (["mix", "compile"], "Compiling project")
        ]
        
        # Add test command
        commands.append((["mix", "test"], "Running tests"))
        
        return self._execute_commands(commands)
    
    def _run_python_commands(self) -> bool:
        """Run Python install and test commands."""
        print("\n🔧 Running Python commands...")
        
        commands = []
        
        # Install dependencies
        install_commands = self._get_python_install_commands()
        commands.extend(install_commands)
        
        # Add test commands
        test_commands = self._get_python_test_commands()
        commands.extend(test_commands)
        
        return self._execute_commands(commands)
    
    def _get_laravel_test_commands(self) -> List[Tuple[List[str], str]]:
        """Get Laravel test commands based on available test runners."""
        commands = []
        
        # Check for PHPUnit
        if (self.project_path / "vendor" / "bin" / "phpunit").exists():
            commands.append((["vendor/bin/phpunit"], "Running PHPUnit tests"))
        elif (self.project_path / "phpunit.xml").exists():
            commands.append((["./vendor/bin/phpunit"], "Running PHPUnit tests"))
        elif self._command_exists("php"):
            # Try artisan test command (Laravel 8+)
            commands.append((["php", "artisan", "test"], "Running Laravel tests"))
        
        return commands
    
    def _get_python_install_commands(self) -> List[Tuple[List[str], str]]:
        """Get Python install commands based on available dependency files."""
        commands = []
        
        if (self.project_path / "requirements.txt").exists():
            commands.append((["pip", "install", "-r", "requirements.txt"], "Installing Python dependencies"))
        elif (self.project_path / "pyproject.toml").exists():
            commands.append((["pip", "install", "-e", "."], "Installing project in development mode"))
        elif (self.project_path / "setup.py").exists():
            commands.append((["pip", "install", "-e", "."], "Installing project in development mode"))
        elif (self.project_path / "Pipfile").exists():
            commands.append((["pipenv", "install", "--dev"], "Installing dependencies with Pipenv"))
        
        return commands
    
    def _get_python_test_commands(self) -> List[Tuple[List[str], str]]:
        """Get Python test commands based on available test frameworks."""
        commands = []
        
        # Check for pytest
        if self._has_python_package("pytest") or (self.project_path / "pytest.ini").exists():
            commands.append((["python", "-m", "pytest"], "Running pytest"))
        # Check for unittest
        elif self._has_test_directory():
            commands.append((["python", "-m", "unittest", "discover"], "Running unittest"))
        # Check for tox
        elif (self.project_path / "tox.ini").exists():
            commands.append((["tox"], "Running tox"))
        
        return commands
    
    def _has_python_package(self, package_name: str) -> bool:
        """Check if a Python package is available."""
        try:
            subprocess.run(["python", "-c", f"import {package_name}"], 
                         check=True, capture_output=True, cwd=self.project_path)
            return True
        except subprocess.CalledProcessError:
            return False
    
    def _has_test_directory(self) -> bool:
        """Check if project has test directories."""
        test_dirs = ["test", "tests", "test_*"]
        for pattern in test_dirs:
            if list(self.project_path.glob(pattern)):
                return True
        return False
    
    def _command_exists(self, command: str) -> bool:
        """Check if a command exists in the system."""
        try:
            subprocess.run(["which", command], check=True, capture_output=True)
            return True
        except subprocess.CalledProcessError:
            return False
    
    def _execute_commands(self, commands: List[Tuple[List[str], str]]) -> bool:
        """Execute a list of commands.
        
        Args:
            commands: List of (command_list, description) tuples
            
        Returns:
            True if all commands succeeded, False otherwise
        """
        all_success = True
        
        for command, description in commands:
            print(f"\n📋 {description}...")
            print(f"Running: {' '.join(command)}")
            
            try:
                result = subprocess.run(
                    command,
                    cwd=self.project_path,
                    check=True,
                    capture_output=True,
                    text=True,
                    timeout=300  # 5 minute timeout
                )
                
                print("✅ Success!")
                if result.stdout.strip():
                    print(f"Output: {result.stdout.strip()}")
                    
            except subprocess.CalledProcessError as e:
                print(f"❌ Command failed with exit code {e.returncode}")
                if e.stdout:
                    print(f"Stdout: {e.stdout}")
                if e.stderr:
                    print(f"Stderr: {e.stderr}")
                all_success = False
                
            except subprocess.TimeoutExpired:
                print("❌ Command timed out after 5 minutes")
                all_success = False
                
            except FileNotFoundError:
                print(f"❌ Command not found: {command[0]}")
                print(f"Make sure {command[0]} is installed and in your PATH")
                all_success = False
        
        return all_success
    
    def get_stack_commands(self, stack: TechStack) -> List[Tuple[List[str], str]]:
        """Get list of commands that would be run for a stack without executing them.
        
        Args:
            stack: The tech stack
            
        Returns:
            List of (command_list, description) tuples
        """
        if stack == TechStack.LARAVEL:
            commands = [
                (["composer", "install"], "Installing PHP dependencies"),
                (["composer", "dump-autoload"], "Generating autoload files")
            ]
            commands.extend(self._get_laravel_test_commands())
            return commands
            
        elif stack == TechStack.PHOENIX:
            return [
                (["mix", "deps.get"], "Installing Elixir dependencies"),
                (["mix", "compile"], "Compiling project"),
                (["mix", "test"], "Running tests")
            ]
            
        elif stack == TechStack.PYTHON:
            commands = []
            commands.extend(self._get_python_install_commands())
            commands.extend(self._get_python_test_commands())
            return commands
            
        else:
            return []