"""Database management commands for CleverAgents CLI.

The ``agents db`` command group exposes Alembic-managed schema operations
so that administrators can inspect, apply, and roll back database migrations.

## Commands

| Command                           | Description                              |
|-----------------------------------|------------------------------------------|
| ``agents db migrate``             | Generate a new migration revision        |
| ``agents db upgrade [REVISION]``  | Apply pending migrations (default: head) |
| ``agents db downgrade REVISION``  | Roll back to a specific revision         |
| ``agents db current``             | Show the current migration revision      |
| ``agents db history``             | Show migration history                   |

These commands delegate to the :class:`MigrationRunner` which wraps the
Alembic ``command`` API with project-aware database URL resolution.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Annotated

import typer

from cleveragents.cli.formatting import format_output

if TYPE_CHECKING:
    from cleveragents.infrastructure.database.migration_runner import MigrationRunner

app = typer.Typer(help="Database migration management.")
_FORMAT_HELP = "Output format: json, yaml, plain, table, or rich (default: rich)"


def _get_runner() -> MigrationRunner:
    """Build a :class:`MigrationRunner` using the resolved database URL."""
    from cleveragents.application.container import get_database_url
    from cleveragents.infrastructure.database.migration_runner import MigrationRunner

    return MigrationRunner(get_database_url())


@app.command()
def migrate(
    message: Annotated[
        str,
        typer.Option(
            "--message",
            "-m",
            help="Short description for the migration revision",
        ),
    ] = "auto",
    fmt: Annotated[str, typer.Option("--format", "-f", help=_FORMAT_HELP)] = "rich",
) -> None:
    """Generate a new Alembic migration revision (autogenerate).

    Compares the current ORM models against the database schema and
    produces a migration script for any detected differences.
    """
    from alembic import command

    runner = _get_runner()
    command.revision(runner.alembic_cfg, message=message, autogenerate=True)
    typer.echo(f"Migration revision created: {message}")


@app.command()
def upgrade(
    revision: Annotated[
        str,
        typer.Argument(help="Target revision (default: 'head')"),
    ] = "head",
    fmt: Annotated[str, typer.Option("--format", "-f", help=_FORMAT_HELP)] = "rich",
) -> None:
    """Apply pending database migrations up to REVISION (default: head).

    On a fresh database this creates the full schema.  On an existing
    database it applies only the migrations that have not yet been run.
    """
    runner = _get_runner()

    if revision == "head":
        runner.init_or_upgrade()
    else:
        from alembic import command

        command.upgrade(runner.alembic_cfg, revision)

    current = runner.get_current_revision()
    data = {"status": "ok", "current_revision": current}
    if fmt == "rich":
        typer.echo(f"Database upgraded to revision: {current}")
    else:
        typer.echo(format_output(data, fmt))


@app.command()
def downgrade(
    revision: Annotated[
        str,
        typer.Argument(help="Target revision to downgrade to (e.g. '-1' or a rev id)"),
    ],
    fmt: Annotated[str, typer.Option("--format", "-f", help=_FORMAT_HELP)] = "rich",
) -> None:
    """Roll the database back to REVISION.

    Accepts Alembic revision identifiers such as a hex revision id,
    ``-1`` (one step back), or ``base`` (roll back everything).

    WARNING: Downgrading may cause data loss.  Always back up the
    database before running this command.
    """
    from alembic import command

    runner = _get_runner()
    command.downgrade(runner.alembic_cfg, revision)

    current = runner.get_current_revision()
    data = {"status": "ok", "current_revision": current}
    if fmt == "rich":
        typer.echo(f"Database downgraded to revision: {current}")
    else:
        typer.echo(format_output(data, fmt))


@app.command()
def current(
    fmt: Annotated[str, typer.Option("--format", "-f", help=_FORMAT_HELP)] = "rich",
) -> None:
    """Show the current migration revision of the database."""
    runner = _get_runner()
    rev = runner.get_current_revision()
    pending = runner.get_pending_migrations()

    data = {
        "current_revision": rev,
        "pending_count": len(pending),
        "pending_revisions": pending,
    }

    if fmt == "rich":
        typer.echo(f"Current revision : {rev or '(none)'}")
        typer.echo(f"Pending migrations: {len(pending)}")
        if pending:
            for p in pending:
                typer.echo(f"  - {p}")
    else:
        typer.echo(format_output(data, fmt))


@app.command()
def history(
    fmt: Annotated[str, typer.Option("--format", "-f", help=_FORMAT_HELP)] = "rich",
) -> None:
    """Show the migration revision history."""
    from alembic import command

    runner = _get_runner()

    if fmt == "rich":
        command.history(runner.alembic_cfg)
    else:
        from alembic.script import ScriptDirectory

        script = ScriptDirectory.from_config(runner.alembic_cfg)
        revisions = [
            {
                "revision": rev.revision,
                "down_revision": rev.down_revision,
                "description": rev.doc or "",
            }
            for rev in script.walk_revisions()
        ]
        typer.echo(format_output(revisions, fmt))
