"""Command-line interface for PymergeticOS builder using Python Fire.

This module provides a class-based CLI interface that wraps the OS builder
functionality. Commands are exposed as methods on the CLI class, which Fire
automatically converts into command-line arguments.

Example:
    Basic usage:
        $ pymergetic-build build --config=pymergetic_os.yaml
        $ pymergetic-build clean
        $ pymergetic-build sample > config.yaml
"""

import os
import sys
import fire
import subprocess
from pathlib import Path
from pymergetic.os.builder.builder import OSBuilder
from pymergetic.os.builder.config import Config
from pymergetic.os.builder.utils import run_command, check_command
from pymergetic.os.builder.console import (
    console, print_success, print_info, print_error, print_warning,
    print_step
)

# Set pager to cat to prevent Fire from using a pager
os.environ['PAGER'] = 'cat'


class CLI:
    """Command-line interface for PymergeticOS builder.
    
    This class provides the main entry point for all CLI commands. Methods
    are automatically exposed as commands by Python Fire.
    
    Attributes:
        All methods are exposed as CLI commands. See individual method
        docstrings for command-specific documentation.
    
    Example:
        The build command can be called as:
            $ pymergetic-build build --config=config.yaml --output=my-iso.iso
        
        Or using the Python module:
            $ python -m pymergetic.os.builder.cli build --config=config.yaml
    """
    
    def build(self, config: str = None, output: str = None, force: bool = False) -> None:
        """Build a PymergeticOS ISO image.
        
        This command orchestrates the complete build process:
        1. Loads configuration from YAML file or environment variable
        2. Creates a Debian chroot environment using debootstrap (cached if possible)
        3. Installs base packages and configures the system
        4. Creates a squashfs filesystem image
        5. Generates boot configuration (GRUB/ISOLINUX)
        6. Builds the final bootable ISO image
        
        The builder uses caching to speed up rebuilds. If only runtime scripts
        (like provisioning scripts) are modified, the chroot is reused from cache.
        Use --force to force a full rebuild.
        
        Args:
            config: Path to YAML configuration file. If not provided, checks
                    the PYMERGETIC_OS_BUILD_CONFIG environment variable. If
                    neither is provided, uses default configuration values.
                    Defaults to None.
            output: Custom output ISO filename. If not provided, defaults to
                    `pymergetic-os-{release}.iso` where {release} is the
                    Debian release name from the configuration. Defaults to None.
            force: Force a full rebuild, ignoring cache. Defaults to False.
        
        Raises:
            FileNotFoundError: If the specified config file does not exist.
            RuntimeError: If required system commands are not available or
                         if the build process fails at any stage.
        
        Example:
            Build with custom config:
                $ pymergetic-build build --config=/path/to/config.yaml
            
            Build with custom output name:
                $ pymergetic-build build --output=my-custom-iso.iso
            
            Force full rebuild (ignore cache):
                $ pymergetic-build build --force
            
            Build using environment variable:
                $ export PYMERGETIC_OS_BUILD_CONFIG=/path/to/config.yaml
                $ pymergetic-build build
        """
        builder = OSBuilder(config_file=config)
        iso_path = builder.build(output_name=output, force=force)
        console.print()
        print_success(f"ISO built: [bold]{iso_path}[/bold]")
    
    def clean(self) -> None:
        """Clean build artifacts and temporary files.
        
        Removes the entire build directory, including:
        - Chroot environment
        - ISO staging directory
        - All intermediate build files
        
        The build directory path is determined from the configuration file
        (via PYMERGETIC_OS_BUILD_CONFIG) or uses the default path.
        
        Note:
            This operation requires sudo privileges as the chroot directory
            is owned by root.
        
        Raises:
            ValueError: If no configuration file is provided and
                      PYMERGETIC_OS_BUILD_CONFIG is not set.
        
        Example:
            Clean build artifacts:
                $ pymergetic-build clean
        """
        config = Config.from_yaml()
        print_info(f"Removing build directory: [bold]{config.build_dir}[/bold]")
        
        if config.build_dir.exists():
            run_command(['rm', '-rf', str(config.build_dir)], sudo=True)
            print_success("Build directory removed")
        else:
            print_info("No build directory to clean")
    
    def sample(self) -> None:
        """Generate and display a sample configuration YAML file.
        
        Outputs a complete YAML configuration file with all default values
        to stdout. This can be piped to a file to create a starting template
        for custom builds.
        
        The sample includes:
        - OS settings (release, hostname, architecture, packages)
        - Build directory settings
        - Module configuration
        
        Example:
            Save sample config to file:
                $ pymergetic-build sample > pymergetic_os.yaml
            
            View sample config:
                $ pymergetic-build sample
            
            Use with custom values:
                $ pymergetic-build sample > config.yaml
                $ # Edit config.yaml with your preferences
                $ pymergetic-build build --config=config.yaml
        """
        # Create default config and output as YAML
        config = Config()
        config.to_yaml(sys.stdout)
    
    def test(self, iso: str = None, memory: int = 2048, kvm: bool = None, display: str = None) -> None:
        """Test the built ISO in QEMU.
        
        Launches QEMU to test the built ISO image. Automatically detects
        the most recent ISO if not specified. Works in WSL2 with or without
        KVM acceleration.
        
        Args:
            iso: Path to ISO file. If not provided, automatically finds the
                most recent ISO in the build directory. Defaults to None.
            memory: Amount of RAM in MB to allocate to the VM. Defaults to 2048.
            kvm: Enable KVM acceleration (faster). If None, auto-detects
                 KVM availability. Defaults to None.
            display: Display backend to use. Options: 'gtk', 'sdl', 'vnc', 'none'.
                    If None, auto-selects based on environment. For WSL, 'vnc'
                    is recommended. Defaults to None.
        
        Raises:
            FileNotFoundError: If ISO file is not found.
            RuntimeError: If QEMU is not installed or cannot be executed.
        
        Example:
            Test with auto-detected ISO:
                $ pymergetic-build test
            
            Test specific ISO:
                $ pymergetic-build test --iso=build/os/pymergetic-os-trixie.iso
            
            Test with VNC (good for WSL):
                $ pymergetic-build test --display=vnc
                # Then connect with: vncviewer localhost:5900
            
            Test with more memory:
                $ pymergetic-build test --memory=4096
            
            Force software emulation (no KVM):
                $ pymergetic-build test --kvm=False
        """
        # Find ISO file
        if not iso:
            config = Config.from_yaml()
            # Look for ISO files in build directory
            build_dir = config.build_dir.parent
            iso_files = list(build_dir.glob("*.iso"))
            
            if not iso_files:
                print_error("No ISO file found. Build an ISO first with: pymergetic-build build")
                sys.exit(1)
            
            # Use most recent ISO
            iso_path = max(iso_files, key=lambda p: p.stat().st_mtime)
            print_info(f"Auto-detected ISO: [bold]{iso_path.name}[/bold]")
        else:
            iso_path = Path(iso)
            if not iso_path.exists():
                print_error(f"ISO file not found: {iso_path}")
                sys.exit(1)
        
        # Check QEMU availability
        if not check_command('qemu-system-x86_64'):
            print_error("QEMU not found. Install with: sudo apt-get install qemu-system-x86")
            sys.exit(1)
        
        # Auto-detect KVM if not specified
        if kvm is None:
            # Check if KVM is available (works in WSL2 with nested virtualization)
            kvm_available = os.path.exists('/dev/kvm') and os.access('/dev/kvm', os.R_OK | os.W_OK)
            kvm = kvm_available
            if kvm:
                print_info("KVM acceleration available")
            else:
                print_warning("KVM not available, using software emulation (slower)")
        
        # Auto-select display backend if not specified
        if display is None:
            # Check if we're in WSL
            is_wsl = False
            try:
                if os.path.exists('/proc/version'):
                    with open('/proc/version', 'r') as f:
                        if 'microsoft' in f.read().lower():
                            is_wsl = True
            except:
                pass
            
            if is_wsl:
                display = 'vnc'  # VNC works best in WSL
                print_info("WSL detected, using VNC display")
            elif os.getenv('DISPLAY'):
                display = 'gtk'  # X11 forwarding available
            else:
                display = 'vnc'  # Default to VNC for headless
        
        # Build QEMU command
        qemu_cmd = [
            'qemu-system-x86_64',
            '-m', str(memory),
            '-cdrom', str(iso_path),
            '-boot', 'd',  # Boot from CD-ROM
        ]
        
        # Add display backend
        if display == 'vnc':
            qemu_cmd.extend(['-display', 'vnc=:0'])  # VNC on port 5900
            print_info("VNC server: [bold]localhost:5900[/bold] (connect with: vncviewer localhost:5900)")
        elif display == 'none':
            qemu_cmd.extend(['-display', 'none'])
            print_warning("Running headless (no display)")
        else:
            qemu_cmd.extend(['-display', display])
        
        # Add KVM acceleration if enabled
        if kvm:
            qemu_cmd.extend(['-accel', 'kvm'])
        else:
            qemu_cmd.extend(['-accel', 'tcg'])
        
        # Additional useful options
        qemu_cmd.extend([
            '-smp', '2',  # 2 CPU cores
            '-netdev', 'user,id=net0',  # User-mode networking
            '-device', 'virtio-net,netdev=net0',  # Virtio network device
        ])
        
        print_step("TEST", f"Starting QEMU with ISO: [bold]{iso_path.name}[/bold]")
        print_info(f"Memory: [bold]{memory} MB[/bold] | KVM: [bold]{'Yes' if kvm else 'No (TCG)'}[/bold] | Display: [bold]{display}[/bold]")
        console.print()
        
        if display == 'vnc':
            print_info("Connect to the VM with: [bold]vncviewer localhost:5900[/bold]")
            print_info("Or use: [bold]remmina[/bold] or any VNC client")
        elif display != 'none':
            print_info("QEMU window will open. Close it or press Ctrl+C to stop the VM.")
        else:
            print_info("VM running headless. Press Ctrl+C to stop.")
        console.print()
        
        try:
            # Run QEMU
            subprocess.run(qemu_cmd, check=False)
        except KeyboardInterrupt:
            console.print()
            print_info("QEMU stopped by user")
        except Exception as e:
            print_error(f"Failed to start QEMU: {e}")
            sys.exit(1)


def main() -> None:
    """Main CLI entry point.
    
    Initializes Python Fire with the CLI class, which automatically
    converts class methods into command-line commands.
    
    This function is called when the module is executed directly or
    when the `pymergetic-build` command is invoked.
    """
    fire.Fire(CLI)


if __name__ == '__main__':
    main()

