"""Rich CLI interface for metapg migrations."""

import asyncio
from pathlib import Path

import typer
from rich.console import Console
from rich.table import Table
from rich.text import Text

from metapg.migrations import MigrationRunner

app = typer.Typer(
    name="metapg",
    help="Meta PostgreSQL pools and raw SQL migrations",
    no_args_is_help=True,
)
console = Console()


def run_async(coro):
    """Run an async function in the CLI."""
    return asyncio.run(coro)


@app.command()
def status(
    db: str = typer.Option("default", "--db", "-d", help="Database name"),
    migrations_dir: str | None = typer.Option(
        None,
        "--migrations-dir",
        help="Migrations directory",
    ),
):
    """Show migration status for a database."""

    async def _status():
        migrations_path = Path(migrations_dir) if migrations_dir else None
        runner = MigrationRunner(db, migrations_path)
        status = await runner.get_status()

        # Create status table
        table = Table(title=f"Migration Status: {db}")
        table.add_column("Migration", style="cyan")
        table.add_column("Status", justify="center")
        table.add_column("Applied", style="green")
        table.add_column("Duration", justify="right")

        # Add applied migrations
        for record in status.applied:
            duration_text = f"{record.duration_ms:.1f}ms" if record.duration_ms else ""
            status_text = (
                Text("✓ Applied", style="green")
                if not record.error
                else Text("✗ Failed", style="red")
            )
            table.add_row(
                record.name,
                status_text,
                str(record.applied_at.strftime("%Y-%m-%d %H:%M")),
                duration_text,
            )

        # Add pending migrations
        for migration in status.pending:
            table.add_row(
                migration.name,
                Text("○ Pending", style="yellow"),
                "",
                "",
            )

        console.print(table)

        # Summary
        if status.is_up_to_date:
            console.print("\n[green]✓ Database is up to date[/green]")
        else:
            console.print(
                f"\n[yellow]○ {len(status.pending)} migrations pending[/yellow]",
            )

    run_async(_status())


@app.command()
def apply(
    db: str = typer.Option("default", "--db", "-d", help="Database name"),
    target: str | None = typer.Option(
        None,
        "--target",
        "-t",
        help="Target migration",
    ),
    migrations_dir: str | None = typer.Option(
        None,
        "--migrations-dir",
        help="Migrations directory",
    ),
    force: bool = typer.Option(
        False,
        "--force",
        "-f",
        help="Apply without confirmation",
    ),
):
    """Apply pending migrations."""

    async def _apply():
        migrations_path = Path(migrations_dir) if migrations_dir else None
        runner = MigrationRunner(db, migrations_path)

        # Check status first
        status = await runner.get_status()

        if not status.pending:
            console.print("[green]✓ Database is up to date[/green]")
            return

        to_apply = status.pending
        if target:
            # Find target
            target_idx = None
            for i, migration in enumerate(to_apply):
                if (
                    migration.name == target
                    or str(migration.version).zfill(3) in target
                ):
                    target_idx = i
                    break

            if target_idx is None:
                console.print(
                    f"[red]Error: Target migration '{target}' not found[/red]",
                )
                raise typer.Exit(1)

            to_apply = to_apply[: target_idx + 1]

        # Show what will be applied
        console.print(f"\n[cyan]Migrations to apply ({db}):[/cyan]")
        for migration in to_apply:
            desc = f" - {migration.description}" if migration.description else ""
            console.print(f"  [yellow]→[/yellow] {migration.name}{desc}")

        # Confirm unless forced
        if not force:
            if not typer.confirm(f"\nApply {len(to_apply)} migrations to '{db}'?"):
                console.print("Cancelled")
                return

        console.print()

        # Apply migrations
        try:
            for migration in to_apply:
                console.print(f"[blue]→[/blue] Applying {migration.name}...")
                await runner.apply_migration(migration)
                console.print(f"[green]✓[/green] Applied {migration.name}")

            console.print(
                f"\n[green]✓ Successfully applied {len(to_apply)} migrations[/green]",
            )

        except Exception as e:
            console.print(f"\n[red]✗ Migration failed: {e}[/red]")
            raise typer.Exit(1)

    run_async(_apply())


@app.command()
def rollback(
    db: str = typer.Option("default", "--db", "-d", help="Database name"),
    steps: int = typer.Option(
        1,
        "--steps",
        "-n",
        help="Number of migrations to rollback",
    ),
    migrations_dir: str | None = typer.Option(
        None,
        "--migrations-dir",
        help="Migrations directory",
    ),
):
    """Rollback applied migrations."""

    async def _rollback():
        migrations_path = Path(migrations_dir) if migrations_dir else None
        runner = MigrationRunner(db, migrations_path)

        # Get applied migrations
        applied = await runner.get_applied_migrations()

        if not applied:
            console.print("[yellow]No migrations to rollback[/yellow]")
            return

        # Get migrations to rollback (most recent first)
        to_rollback = applied[-steps:] if steps <= len(applied) else applied
        to_rollback.reverse()

        console.print(f"\n[cyan]Migrations to rollback ({db}):[/cyan]")
        for record in to_rollback:
            console.print(f"  [yellow]←[/yellow] {record.name}")

        if not typer.confirm(f"\nRollback {len(to_rollback)} migrations from '{db}'?"):
            console.print("Cancelled")
            return

        console.print()

        # Rollback migrations
        try:
            for record in to_rollback:
                console.print(f"[blue]←[/blue] Rolling back {record.name}...")
                await runner.rollback_migration(record.name)
                console.print(f"[green]✓[/green] Rolled back {record.name}")

            console.print(
                f"\n[green]✓ Successfully rolled back {len(to_rollback)} migrations[/green]",
            )

        except Exception as e:
            console.print(f"\n[red]✗ Rollback failed: {e}[/red]")
            raise typer.Exit(1)

    run_async(_rollback())


@app.command()
def create(
    name: str = typer.Argument(..., help="Migration name (e.g., 'add_user_index')"),
    db: str = typer.Option("default", "--db", "-d", help="Database name"),
    migrations_dir: str | None = typer.Option(
        None,
        "--migrations-dir",
        help="Migrations directory",
    ),
):
    """Create a new migration file."""
    migrations_path = (
        Path(migrations_dir) if migrations_dir else Path("migrations") / db
    )
    migrations_path.mkdir(parents=True, exist_ok=True)

    # Find next version number
    existing = list(migrations_path.glob("*.sql"))
    if existing:
        versions = []
        for f in existing:
            try:
                version = int(f.stem.split("_")[0])
                versions.append(version)
            except (ValueError, IndexError):
                continue
        next_version = max(versions) + 1 if versions else 1
    else:
        next_version = 1

    # Create filename
    clean_name = name.replace(" ", "_").replace("-", "_").lower()
    filename = f"{next_version:03d}_{clean_name}.sql"
    file_path = migrations_path / filename

    # Create template content
    template = f"""-- {name.replace("_", " ").title()}
-- Created: {asyncio.run(asyncio.create_task(asyncio.coroutine(lambda: "2024-01-01")()))}

-- Your migration SQL here
-- CREATE TABLE ...

-- ROLLBACK (optional)
-- DROP TABLE ...
"""

    file_path.write_text(template)
    console.print(f"[green]✓[/green] Created migration: {file_path}")


if __name__ == "__main__":
    app()
