"""
Database URL resolution, migrations path, and Alembic configuration.

Used by db/cli.py, db/seed.py, and init/upgrade flows.
"""

from __future__ import annotations

import os
from pathlib import Path

try:
    from alembic.config import Config
    from sqlalchemy import text

    ALEMBIC_AVAILABLE = True
except ImportError:
    ALEMBIC_AVAILABLE = False
    Config = None  # type: ignore[misc, assignment]

from pycharter.config import get_database_url, set_database_url


def require_alembic() -> None:
    """Raise ImportError if Alembic is not available."""
    if not ALEMBIC_AVAILABLE:
        raise ImportError(
            "alembic and sqlalchemy are required. Install with: pip install alembic sqlalchemy"
        )


def get_db_url(database_url: str | None = None, required: bool = True) -> str | None:
    """Resolve database URL from argument, config, or env. Defaults to sqlite:///pycharter.db if unset."""
    db_url = database_url or get_database_url() or os.getenv("PYCHARTER_DATABASE_URL")
    if not db_url:
        default_db_path = Path.cwd() / "pycharter.db"
        db_url = f"sqlite:///{default_db_path}"
        if required:
            print(f"ℹ Using default SQLite database: {db_url}")
    return db_url


def get_migrations_dir() -> Path:
    """Return the path to db/migrations (package-relative or fallback)."""
    try:
        import pycharter

        migrations_dir = Path(pycharter.__file__).parent / "db" / "migrations"
    except (ImportError, AttributeError):
        migrations_dir = Path(__file__).resolve().parent / "migrations"
    if not migrations_dir.exists():
        cwd_migrations = Path(os.getcwd()) / "pycharter" / "db" / "migrations"
        if cwd_migrations.exists():
            return cwd_migrations
        raise FileNotFoundError(
            f"Migrations directory not found. Tried:\n  - {migrations_dir}\n  - {cwd_migrations}"
        )
    return migrations_dir


def get_alembic_config(database_url: str | None = None) -> "Config":
    """Build Alembic Config with script_location and sqlalchemy.url set."""
    config = Config()
    config.set_main_option("script_location", str(get_migrations_dir()))
    config.set_main_option("prepend_sys_path", ".")
    config.set_main_option("version_path_separator", "os")
    config.set_main_option(
        "file_template",
        "%%(year)d%%(month).2d%%(day).2d%%(hour).2d%%(minute).2d%%(second).2d_%%(rev)s_%%(slug)s",
    )
    db_url = database_url or get_database_url() or os.getenv("PYCHARTER_DATABASE_URL")
    if not db_url:
        db_url = f"sqlite:///{Path.cwd() / 'pycharter.db'}"
    config.set_main_option("sqlalchemy.url", db_url)
    set_database_url(db_url)
    return config


def detect_db_type(database_url: str) -> str:
    """Return 'mongodb', 'postgresql', or 'sqlite' from connection string."""
    if database_url.startswith(("mongodb://", "mongodb+srv://")):
        return "mongodb"
    if database_url.startswith(("postgresql://", "postgres://")):
        return "postgresql"
    if database_url.startswith("sqlite://"):
        return "sqlite"
    return "sqlite"


def cleanup_stale_alembic_version(engine) -> None:
    """Drop alembic_version from pycharter and public schema so migrations can run cleanly."""
    with engine.connect() as conn:
        try:
            conn.execute(
                text('DROP TABLE IF EXISTS "pycharter".alembic_version CASCADE')
            )
            conn.commit()
        except Exception:
            try:
                conn.rollback()
            except Exception:
                pass
        try:
            conn.execute(text("DROP TABLE IF EXISTS alembic_version CASCADE"))
            conn.commit()
        except Exception:
            try:
                conn.rollback()
            except Exception:
                pass


def check_and_cleanup_invalid_alembic_version(engine, config: "Config") -> bool:
    """
    Ensure alembic_version in pycharter schema has a valid revision; clean up if invalid or in public.
    Returns True if valid, False otherwise.
    """
    has_table = False
    db_revision = None
    try:
        with engine.connect() as conn:
            try:
                result = conn.execute(
                    text('SELECT version_num FROM "pycharter".alembic_version LIMIT 1')
                )
                db_revision = result.fetchone()
                has_table = True
            except Exception:
                try:
                    result = conn.execute(
                        text("SELECT version_num FROM alembic_version LIMIT 1")
                    )
                    db_revision = result.fetchone()
                    print(
                        "⚠ Warning: Found alembic_version in public schema (should be in pycharter schema)"
                    )
                    print("   Cleaning up from public schema...")
                    cleanup_stale_alembic_version(engine)
                    return False
                except Exception:
                    return False
    except Exception:
        return False
    if not has_table:
        return False
    if not db_revision or not db_revision[0]:
        print("ℹ Found empty alembic_version table in pycharter schema, cleaning up...")
        cleanup_stale_alembic_version(engine)
        return False
    try:
        from alembic.script import ScriptDirectory

        script = ScriptDirectory.from_config(config)
        script.get_revision(db_revision[0])
        return True
    except Exception:
        print(
            f"ℹ Found stale alembic_version with invalid revision '{db_revision[0]}' in pycharter schema"
        )
        print("   Cleaning up stale alembic_version table...")
        cleanup_stale_alembic_version(engine)
        return False
