from __future__ import annotations

import os
from typing import List, Optional

import typer

from svc_infra.db.sql.core import current as core_current
from svc_infra.db.sql.core import downgrade as core_downgrade
from svc_infra.db.sql.core import history as core_history
from svc_infra.db.sql.core import init_alembic as core_init_alembic
from svc_infra.db.sql.core import merge_heads as core_merge_heads
from svc_infra.db.sql.core import revision as core_revision
from svc_infra.db.sql.core import setup_and_migrate as core_setup_and_migrate
from svc_infra.db.sql.core import stamp as core_stamp
from svc_infra.db.sql.core import upgrade as core_upgrade


def apply_database_url(database_url: Optional[str]) -> None:
    """If provided, set SQL_URL for the current process."""
    if database_url:
        os.environ["SQL_URL"] = database_url


def cmd_init(
    database_url: Optional[str] = typer.Option(
        None,
        help="Database URL; overrides env SQL_URL for this command. "
        "Async vs sync is auto-detected from the URL.",
    ),
    discover_packages: Optional[List[str]] = typer.Option(
        None,
        help="Packages to search for SQLAlchemy metadata; may pass multiple. "
        "If omitted, automatic discovery is used.",
    ),
    overwrite: bool = typer.Option(False, help="Overwrite existing files if present."),
):
    """
    Initialize Alembic scaffold. The env.py variant (async vs. sync) is
    auto-detected from SQL_URL (if available at init time).
    """
    apply_database_url(database_url)
    core_init_alembic(
        discover_packages=discover_packages,
        overwrite=overwrite,
    )


def cmd_revision(
    message: str = typer.Option(..., "-m", "--message", help="Revision message."),
    database_url: Optional[str] = typer.Option(
        None, help="Database URL; overrides env for this command."
    ),
    autogenerate: bool = typer.Option(False, help="Autogenerate migrations by comparing metadata."),
    head: Optional[str] = typer.Option("head", help="Set the head to base this revision on."),
    branch_label: Optional[str] = typer.Option(None, help="Branch label."),
    version_path: Optional[str] = typer.Option(None, help="Alternative versions/ path."),
    sql: bool = typer.Option(False, help="Don't generate Python; dump SQL to stdout."),
):
    """Create a new Alembic revision, either empty or autogenerated."""
    apply_database_url(database_url)
    core_revision(
        message=message,
        autogenerate=autogenerate,
        head=head,
        branch_label=branch_label,
        version_path=version_path,
        sql=sql,
    )


def cmd_upgrade(
    revision_target: str = typer.Argument("head", help="Target revision (default head)."),
    database_url: Optional[str] = typer.Option(
        None, help="Database URL; overrides env for this command."
    ),
):
    """Upgrade to a later version."""
    apply_database_url(database_url)
    core_upgrade(revision_target=revision_target)


def cmd_downgrade(
    revision_target: str = typer.Argument("-1", help="Target revision (default -1)."),
    database_url: Optional[str] = typer.Option(
        None, help="Database URL; overrides env for this command."
    ),
):
    """Downgrade to an earlier version."""
    apply_database_url(database_url)
    core_downgrade(revision_target=revision_target)


def cmd_current(
    database_url: Optional[str] = typer.Option(
        None, help="Database URL; overrides env for this command."
    ),
    verbose: bool = typer.Option(False, help="Verbose output."),
):
    """Display the current revision for each database."""
    apply_database_url(database_url)
    core_current(verbose=verbose)


def cmd_history(
    database_url: Optional[str] = typer.Option(
        None, help="Database URL; overrides env for this command."
    ),
    verbose: bool = typer.Option(False, help="Verbose output."),
):
    """List changeset scripts in chronological order."""
    apply_database_url(database_url)
    core_history(verbose=verbose)


def cmd_stamp(
    revision_target: str = typer.Argument("head"),
    database_url: Optional[str] = typer.Option(
        None, help="Database URL; overrides env for this command."
    ),
):
    """Stamp the revision table with the given revision; don't run migrations."""
    apply_database_url(database_url)
    core_stamp(revision_target=revision_target)


def cmd_merge_heads(
    database_url: Optional[str] = typer.Option(
        None, help="Database URL; overrides env for this command."
    ),
    message: Optional[str] = typer.Option(None, "-m", "--message", help="Merge revision message."),
):
    """Create a merge revision for multiple heads."""
    apply_database_url(database_url)
    core_merge_heads(message=message)


def cmd_setup_and_migrate(
    database_url: Optional[str] = typer.Option(
        None,
        help="Overrides env for this command. Async vs sync is auto-detected from the URL.",
    ),
    overwrite_scaffold: bool = typer.Option(False, help="Overwrite alembic scaffold if present."),
    create_db_if_missing: bool = typer.Option(True, help="Create the database/schema if missing."),
    create_followup_revision: bool = typer.Option(
        True, help="Create an autogen follow-up revision if revisions already exist."
    ),
    initial_message: str = typer.Option("initial schema"),
    followup_message: str = typer.Option("autogen"),
):
    """
    End-to-end: ensure DB exists, scaffold Alembic, create/upgrade revisions.
    Async vs. sync is inferred from SQL_URL.
    """
    apply_database_url(database_url)
    core_setup_and_migrate(
        overwrite_scaffold=overwrite_scaffold,
        create_db_if_missing=create_db_if_missing,
        create_followup_revision=create_followup_revision,
        initial_message=initial_message,
        followup_message=followup_message,
    )


def register(app: typer.Typer) -> None:
    app.command("sql-init")(cmd_init)
    app.command("sql-revision")(cmd_revision)
    app.command("sql-upgrade")(cmd_upgrade)
    app.command("sql-downgrade")(cmd_downgrade)
    app.command("sql-current")(cmd_current)
    app.command("sql-history")(cmd_history)
    app.command("sql-stamp")(cmd_stamp)
    app.command("sql-merge-heads")(cmd_merge_heads)
    app.command("sql-setup-and-migrate")(cmd_setup_and_migrate)
