#!/usr/bin/env python
"""
FivcCliche CLI

Command-line interface for FivcCliche - a production-ready, multi-user backend
framework for AI agents built with FastAPI and SQLModel.
"""

import asyncio
import os
import shutil
from pathlib import Path
from typing import cast

import typer
from rich.console import Console
from rich.panel import Panel
from rich.text import Text

from fivcglue import query_component, IComponentSite
from fivccliche import __version__
from fivccliche.services.implements import service_site
from fivccliche.services.interfaces.modules import IModuleSite
from fivccliche.services.interfaces.db import IDatabase
from fivccliche.services.interfaces.auth import IUserAuthenticator

cli = typer.Typer(
    name="FivcCliche",
    help="FivcCliche - Production-ready AI agent backend framework",
    rich_markup_mode="rich",
)

console = Console()

modules = query_component(cast(IComponentSite, service_site), IModuleSite)

# FastAPI App for ASGI
app = modules.create_application()


@cli.command()
def run(
    host: str = typer.Option("0.0.0.0", "--host", "-h", help="Host to bind the server to"),
    port: int = typer.Option(8000, "--port", "-p", help="Port to run the server on"),
    reload: bool = typer.Option(
        True, "--reload/--no-reload", help="Enable auto-reload on code changes"
    ),
    verbose: bool = typer.Option(False, "--verbose", "-v", help="Enable verbose output"),
    dry_run: bool = typer.Option(
        False, "--dry-run", help="Show what would be done without executing"
    ),
):
    """
    Run the FivcCliche FastAPI application
    """
    console.print(
        Panel.fit(
            Text("FivcCliche Server", style="bold blue"),
            subtitle="Production-ready AI agent backend",
        )
    )

    if dry_run:
        console.print("[yellow]DRY RUN:[/yellow] Would start FivcCliche server")
        console.print(f"[yellow]Host:[/yellow] {host}")
        console.print(f"[yellow]Port:[/yellow] {port}")
        console.print(f"[yellow]Reload:[/yellow] {reload}")
        return

    try:
        console.print(f"[blue]Starting server at http://{host}:{port}[/blue]")
        console.print("[yellow]Press Ctrl+C to stop the server[/yellow]")

        if verbose:
            console.print("[cyan]Verbose mode enabled[/cyan]")

        modules.run_application(app, host=host, port=port)

    except KeyboardInterrupt:
        console.print("\n[yellow]Server stopped by user[/yellow]")
    except Exception as e:
        console.print(f"[red]❌ Error running server: {e}[/red]")
        raise typer.Exit(1) from e


@cli.command()
def info():
    """
    Show information about FivcCliche
    """
    info_text = f"""
    [bold blue]FivcCliche[/bold blue] v{__version__}

    A production-ready, multi-user backend framework for AI agents.
    Built with FastAPI and SQLModel for high-performance, type-safe
    async operations that handle concurrent AI agent requests at scale.

    [bold]Features:[/bold]
    • Production-ready multi-user backend
    • Designed for AI agent interactions
    • Async architecture for concurrent request handling
    • Type-safe with Pydantic 2.0 validation
    • SQLModel ORM for reliable data persistence
    • Built on FastAPI for high performance
    • Modular architecture with component system

    [bold]Usage Examples:[/bold]
    fivccliche migrate                                # Initialize database tables
    fivccliche createsuperuser                        # Create admin account
    fivccliche run                                    # Start server
    fivccliche run --port 9000                        # Custom port
    fivccliche run --host 127.0.0.1 --no-reload      # Production mode
    fivccliche info                                   # Show this information
    fivccliche clean                                  # Clean temporary files
    """

    console.print(Panel(info_text, title="FivcCliche", border_style="blue"))


@cli.command()
def clean():
    """
    Clean up temporary files and cache generated by FivcCliche
    """
    console.print("[yellow]Cleaning up temporary files...[/yellow]")

    try:
        # Define directories to clean
        cache_dirs = [
            ".pytest_cache",
            ".mypy_cache",
            ".ruff_cache",
            "__pycache__",
            ".coverage",
            "htmlcov",
        ]

        cleaned_count = 0

        for cache_dir in cache_dirs:
            cache_path = Path(cache_dir)
            if cache_path.exists():
                try:
                    if cache_path.is_dir():
                        shutil.rmtree(cache_path)
                    else:
                        cache_path.unlink()
                    console.print(f"[green]✅ Removed: {cache_dir}[/green]")
                    cleaned_count += 1
                except Exception as e:
                    console.print(f"[yellow]⚠️  Could not remove {cache_dir}: {e}[/yellow]")

        # Clean Python cache files
        for root, _dirs, files in os.walk("."):
            for file in files:
                if file.endswith(".pyc"):
                    try:
                        os.remove(os.path.join(root, file))
                        cleaned_count += 1
                    except Exception:
                        pass

        console.print("\n" + "=" * 60)
        console.print("[bold cyan]Cleanup Summary[/bold cyan]")
        console.print("=" * 60)
        console.print(f"[green]Items cleaned: {cleaned_count}[/green]")
        console.print("[green]✅ Cleanup completed successfully![/green]")

    except Exception as e:
        console.print(f"[red]❌ Error during cleanup: {e}[/red]")
        raise typer.Exit(1) from e


@cli.command()
def migrate():
    """
    Initialize and create database tables.

    This command creates all database tables defined in SQLModel models.
    Run this command before using other commands that require database access.
    """
    console.print(
        Panel.fit(
            Text("Database Migration", style="bold blue"),
            subtitle="Initialize database tables",
        )
    )

    try:
        asyncio.run(_migrate_async())
    except typer.Exit:
        raise
    except Exception as e:
        console.print(f"[red]❌ Unexpected error: {e}[/red]")
        raise typer.Exit(1) from e


async def _migrate_async() -> None:
    """
    Async helper function to initialize database tables.
    """
    try:
        console.print("[cyan]Initializing database tables...[/cyan]")

        # Get database service
        db_service = query_component(cast(IComponentSite, service_site), IDatabase)

        # Create all database tables
        async with db_service.get_engine().begin() as conn:
            await conn.run_sync(db_service.get_metadata().create_all)

        console.print("\n" + "=" * 60)
        console.print("[bold green]✅ Database tables created successfully![/bold green]")
        console.print("=" * 60)

    except typer.Exit:
        raise
    except Exception as e:
        console.print(f"[red]❌ Database error: {e}[/red]")
        raise typer.Exit(1) from e


@cli.command()
def createsuperuser():
    """
    Create a superuser (admin) account interactively.

    This command prompts for username, email, and password to create
    a new superuser account with admin privileges.
    """
    console.print(
        Panel.fit(
            Text("Create Superuser", style="bold blue"),
            subtitle="Create a new admin account",
        )
    )

    try:
        # Prompt for username
        username = typer.prompt("Username")
        if not username or not username.strip():
            console.print("[red]❌ Username cannot be empty[/red]")
            raise typer.Exit(1)

        # Prompt for email
        email = typer.prompt("Email address")
        if not email or not email.strip():
            console.print("[red]❌ Email cannot be empty[/red]")
            raise typer.Exit(1)

        # Prompt for password with confirmation
        password = typer.prompt("Password", hide_input=True)
        if not password or not password.strip():
            console.print("[red]❌ Password cannot be empty[/red]")
            raise typer.Exit(1)

        password_confirm = typer.prompt("Confirm password", hide_input=True)
        if password != password_confirm:
            console.print("[red]❌ Passwords do not match[/red]")
            raise typer.Exit(1)

        # Run the async creation
        asyncio.run(_create_superuser_async(username, email, password))

    except typer.Abort as e:
        console.print("[yellow]Superuser creation cancelled[/yellow]")
        raise typer.Exit(0) from e
    except typer.Exit:
        raise
    except Exception as e:
        console.print(f"[red]❌ Unexpected error: {e}[/red]")
        raise typer.Exit(1) from e


async def _create_superuser_async(username: str, email: str, password: str) -> None:
    """
    Async helper function to create a superuser.

    Args:
        username: Username for the superuser
        email: Email address for the superuser
        password: Password for the superuser
    """
    try:
        # Get database and authenticator services
        db_service = query_component(cast(IComponentSite, service_site), IDatabase)
        auth_service = query_component(cast(IComponentSite, service_site), IUserAuthenticator)

        # Get a database session
        session = db_service.create_session()

        try:
            # Check if user already exists
            from fivccliche.modules.users.methods import get_user_async

            existing_user = await get_user_async(session, username=username)
            if existing_user:
                console.print(f"[red]❌ User '{username}' already exists[/red]")
                raise typer.Exit(1)

            existing_email = await get_user_async(session, email=email)
            if existing_email:
                console.print(f"[red]❌ Email '{email}' is already in use[/red]")
                raise typer.Exit(1)

            # Create the superuser
            user = await auth_service.create_user_async(
                username=username,
                email=email,
                password=password,
                is_superuser=True,
                session=session,
            )

            if user:
                console.print("\n" + "=" * 60)
                console.print("[bold green]✅ Superuser created successfully![/bold green]")
                console.print("=" * 60)
                console.print(f"[cyan]Username:[/cyan] {user.username}")
                console.print(f"[cyan]Email:[/cyan] {user.email}")
                console.print("[cyan]Admin:[/cyan] Yes")
                console.print("=" * 60)
            else:
                console.print("[red]❌ Failed to create superuser[/red]")
                raise typer.Exit(1)

        finally:
            await session.close()

    except typer.Exit:
        raise
    except ValueError as e:
        console.print(f"[red]❌ Validation error: {e}[/red]")
        raise typer.Exit(1) from e
    except Exception as e:
        console.print(f"[red]❌ Database error: {e}[/red]")
        raise typer.Exit(1) from e


@cli.command()
def changepassword():
    """
    Change a user's password interactively.

    This command prompts for a username and new password to update
    an existing user's password.
    """
    console.print(
        Panel.fit(
            Text("Change Password", style="bold blue"),
            subtitle="Update user password",
        )
    )

    try:
        # Prompt for username
        username = typer.prompt("Username")
        if not username or not username.strip():
            console.print("[red]❌ Username cannot be empty[/red]")
            raise typer.Exit(1)

        # Prompt for new password with confirmation
        new_password = typer.prompt("New password", hide_input=True)
        if not new_password or not new_password.strip():
            console.print("[red]❌ Password cannot be empty[/red]")
            raise typer.Exit(1)

        password_confirm = typer.prompt("Confirm new password", hide_input=True)
        if new_password != password_confirm:
            console.print("[red]❌ Passwords do not match[/red]")
            raise typer.Exit(1)

        # Run the async update
        asyncio.run(_change_password_async(username, new_password))

    except typer.Abort as e:
        console.print("[yellow]Password change cancelled[/yellow]")
        raise typer.Exit(0) from e
    except typer.Exit:
        raise
    except Exception as e:
        console.print(f"[red]❌ Unexpected error: {e}[/red]")
        raise typer.Exit(1) from e


async def _change_password_async(username: str, new_password: str) -> None:
    """
    Async helper function to change a user's password.

    Args:
        username: Username of the user to update
        new_password: New password to set
    """
    try:
        # Get database service
        db_service = query_component(cast(IComponentSite, service_site), IDatabase)

        # Get a database session
        session = db_service.create_session()

        try:
            from fivccliche.modules.users.methods import get_user_async, update_user_async

            # Look up user
            user = await get_user_async(session, username=username)
            if not user:
                console.print(f"[red]❌ User '{username}' not found[/red]")
                raise typer.Exit(1)

            # Update password
            await update_user_async(session, user, password=new_password)

            console.print("\n" + "=" * 60)
            console.print("[bold green]✅ Password changed successfully![/bold green]")
            console.print("=" * 60)
            console.print(f"[cyan]Username:[/cyan] {user.username}")
            console.print("=" * 60)

        finally:
            await session.close()

    except typer.Exit:
        raise
    except Exception as e:
        console.print(f"[red]❌ Database error: {e}[/red]")
        raise typer.Exit(1) from e


def main():
    """
    Main entry point for the CLI
    """
    cli()


if __name__ == "__main__":
    main()
