#!/usr/bin/env -S uv run --script
#MISE silent=true
#MISE description="Create a new project from our templates"
# /// script
# dependencies = ["rich", "pyyaml"]
# ///
"""
Slick app generator script with Rich formatting
"""

import sys
import subprocess
import shutil
import yaml
from pathlib import Path

from rich.console import Console
from rich.panel import Panel
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TimeElapsedColumn
from rich.text import Text
from rich.prompt import Prompt
from rich.table import Table
from rich import box

console = Console()

def show_banner():
    """Display a stylish banner"""
    banner = Text("🚀 RAG TEMPLATE", style="bold magenta")
    banner.stylize("bold cyan", 0, 4)  # Style the rocket emoji

    panel = Panel(
        banner,
        box=box.DOUBLE,
        border_style="bright_blue",
        padding=(1, 2)
    )
    console.print(panel)

def validate_app_name(app_name):
    """Validate the app name"""
    if not app_name:
        return False, "App name cannot be empty"

    if not app_name.replace('_', '').replace('-', '').isalnum():
        return False, "App name can only contain letters, numbers, hyphens, and underscores"

    if len(app_name) < 2:
        return False, "App name must be at least 2 characters long"

    if len(app_name) > 50:
        return False, "App name must be less than 50 characters long"

    return True, "Valid app name"

def run_command(command, description, cwd=None):
    """Run a command with progress indicator"""
    try:
        with Progress(
            SpinnerColumn(),
            TextColumn("[progress.description]{task.description}"),
            BarColumn(),
            TimeElapsedColumn(),
            console=console,
            transient=True
        ) as progress:
            task = progress.add_task(description, total=None)

            result = subprocess.run(
                command,
                shell=True,
                cwd=cwd,
                capture_output=True,
                text=True
            )

            progress.update(task, completed=True)

        if result.returncode != 0:
            console.print(f"❌ [red]Error running command:[/red] {command}")
            console.print(f"[red]Error output:[/red] {result.stderr}")
            return False

        return True
    except Exception as e:
        console.print(f"❌ [red]Exception occurred:[/red] {str(e)}")
        return False

class CustomYAMLDumper(yaml.SafeDumper):
    """Custom YAML dumper that preserves list formatting"""

    def write_line_break(self, data=None):
        super().write_line_break(data)
        if len(self.indents) == 1:
            super().write_line_break()

def custom_yaml_dump(data, stream=None):
    """Custom YAML dump function that maintains proper list formatting"""
    class ListDumper(yaml.SafeDumper):
        def increase_indent(self, flow=False, indentless=False):
            return super().increase_indent(flow, False)

        def represent_list(self, data):
            return self.represent_sequence('tag:yaml.org,2002:seq', data, flow_style=False)

        def represent_dict(self, data):
            return self.represent_mapping('tag:yaml.org,2002:map', data, flow_style=False)

    ListDumper.add_representer(list, ListDumper.represent_list)
    ListDumper.add_representer(dict, ListDumper.represent_dict)

    return yaml.dump(data, stream=stream, Dumper=ListDumper,
                    default_flow_style=False, indent=2, sort_keys=False,
                    allow_unicode=True, width=1000)

def find_available_port(compose_path):
    """Find the next available port for the new service"""
    used_ports = set()

    try:
        with open(compose_path, 'r') as f:
            compose_data = yaml.safe_load(f)

        # Extract used ports from existing services
        services = compose_data.get('services', {})
        for service_name, service_config in services.items():
            if service_name == 'traefik':  # Skip traefik
                continue
            ports = service_config.get('ports', [])
            for port_mapping in ports:
                if isinstance(port_mapping, str):
                    external_port = int(port_mapping.split(':')[0])
                    used_ports.add(external_port)

        # Find next available port starting from 8001
        port = 8001
        while port in used_ports:
            port += 1

        return port
    except Exception as e:
        console.print(f"⚠️  [yellow]Could not determine available port, using 8001:[/yellow] {str(e)}")
        return 8001

def generate_service_config(app_name, port):
    """Generate Docker Compose service configuration for new app"""
    return {
        'image': f'mayacontigo/{app_name}:latest',
        'build': {
            'context': '.',
            'dockerfile': '.containers/unit/Dockerfile',
            'args': {
                'PACKAGE': app_name
            }
        },
        'x-bake': {
            'tags': [f'mayacontigo/{app_name}:latest']
        },
        'ports': [f'{port}:80'],
        'labels': [
            'traefik.enable=true',
            f'traefik.http.routers.{app_name}.rule=PathPrefix(`/api/maya{app_name}`)',
            f'traefik.http.routers.{app_name}.entrypoints=web',
            f'traefik.http.routers.{app_name}.middlewares={app_name}-strip',
            f'traefik.http.middlewares.{app_name}-strip.stripprefix.prefixes=/api/maya{app_name}'
        ]
    }

def add_service_to_compose(app_name):
    """Add new service to compose.yaml file"""
    compose_path = Path('compose.yaml')

    if not compose_path.exists():
        console.print(f"⚠️  [yellow]compose.yaml not found, skipping service addition.[/yellow]")
        return True

    try:
        # Create backup
        backup_path = compose_path.with_suffix('.yaml.backup')
        shutil.copy2(compose_path, backup_path)

        # Load existing compose file
        with open(compose_path, 'r') as f:
            compose_data = yaml.safe_load(f)

        # Check if service already exists
        services = compose_data.get('services', {})
        if app_name in services:
            console.print(f"⚠️  [yellow]Service '{app_name}' already exists in compose.yaml[/yellow]")
            return True

        # Find available port
        port = find_available_port(compose_path)

        # Generate service configuration
        service_config = generate_service_config(app_name, port)

        # Add service to compose data
        services[app_name] = service_config

        # Write updated compose file with proper formatting
        with open(compose_path, 'w') as f:
            custom_yaml_dump(compose_data, f)

        console.print(f"✅ [green]Added service '{app_name}' to compose.yaml (port {port})[/green]")

        # Remove backup on success
        backup_path.unlink()

        return True

    except Exception as e:
        console.print(f"❌ [red]Failed to add service to compose.yaml:[/red] {str(e)}")

        # Restore backup if it exists
        if backup_path.exists():
            shutil.copy2(backup_path, compose_path)
            backup_path.unlink()
            console.print("🔄 [yellow]Restored original compose.yaml from backup[/yellow]")

        return False

def show_summary(app_name):
    """Show a summary of what was created"""
    table = Table(title="🎉 App Created Successfully!", box=box.ROUNDED)
    table.add_column("Component", style="cyan", no_wrap=True)
    table.add_column("Status", style="green")
    table.add_column("Location", style="yellow")

    table.add_row("📁 Project Directory", "✅ Created", f"./apps/{app_name}")
    table.add_row("📦 Dependencies", "✅ Installed", "package.json + requirements.txt")
    table.add_row("🔧 Template", "✅ Applied", "RAG template")
    table.add_row("🐍 Python Environment", "✅ Synced", "uv environment")
    table.add_row("🐳 Docker Service", "✅ Added", "compose.yaml")

    console.print(table)

    # Next steps
    next_steps = Panel(
        f"[bold green]Next Steps:[/bold green]\n\n"
        f"1. [cyan]mise dev --app {app_name}[/cyan]\n"
        f"2. [cyan]Start building your RAG app! 🚀[/cyan]",
        title="🎯 What's Next?",
        border_style="green",
        box=box.ROUNDED
    )
    console.print(next_steps)

def main():
    """Main function"""
    show_banner()

    # Get app name from command line or prompt
    if len(sys.argv) < 2:
        console.print("[yellow]No app name provided as argument.[/yellow]")
        app_name = Prompt.ask("Enter your app name", default="my-rag-app")
    else:
        app_name = sys.argv[1]

    # Validate app name
    is_valid, message = validate_app_name(app_name)
    if not is_valid:
        console.print(f"❌ [red]Invalid app name:[/red] {message}")
        sys.exit(1)

    # Show what we're about to do
    console.print(f"[bold]Creating app:[/bold] [cyan]{app_name}[/cyan]")
    console.print()

    # Check if directory already exists
    app_path = Path(f"apps/{app_name}")
    if app_path.exists():
        console.print(f"⚠️  [yellow]Directory 'apps/{app_name}' already exists![/yellow]")
        if not Prompt.ask("Do you want to continue?", choices=["y", "n"], default="n") == "y":
            console.print("Operation cancelled.")
            sys.exit(0)

    # Step 1: Copy template
    console.print("📂 [bold]Step 1:[/bold] Copying RAG template...")
    if not run_command(
        f"uvx copier copy .templates/rag apps --data project_name={app_name} --trust",
        "Copying template files..."
    ):
        console.print("❌ [red]Failed to copy template. Make sure you're in the right directory.[/red]")
        sys.exit(1)

    # Step 2: Install npm dependencies
    console.print("📦 [bold]Step 2:[/bold] Installing npm dependencies...")
    if not run_command(
        "pnpm install",
        "Installing npm packages...",
        cwd=f"apps/{app_name}"
    ):
        console.print("❌ [red]Failed to install npm dependencies.[/red]")
        sys.exit(1)

    # Step 3: Sync Python environment
    console.print("🐍 [bold]Step 3:[/bold] Setting up Python environment...")
    if not run_command(
        "uv sync",
        "Syncing Python environment...",
        cwd=f"apps/{app_name}"
    ):
        console.print("❌ [red]Failed to sync Python environment.[/red]")
        sys.exit(1)

    # Step 4: Add service to compose.yaml
    console.print("🐳 [bold]Step 4:[/bold] Adding service to compose.yaml...")
    if not add_service_to_compose(app_name):
        console.print("❌ [red]Failed to add service to compose.yaml.[/red]")
        console.print("⚠️  [yellow]You can manually add the service later.[/yellow]")
        # Don't exit here, this is not critical

    # Success!
    console.print()
    show_summary(app_name)

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        console.print("\n[yellow]Operation cancelled by user.[/yellow]")
        sys.exit(1)
    except Exception as e:
        console.print(f"\n❌ [red]Unexpected error:[/red] {str(e)}")
        sys.exit(1)
