"""
CLI for database management: init, upgrade, downgrade, seed, truncate.

Pattern: airflow db init / airflow db upgrade. Delegates URL and Alembic to db.config, seed to db.seed.
"""

from __future__ import annotations

import argparse
import sys
from pathlib import Path

try:
    from alembic import command
    from sqlalchemy import create_engine, inspect, text
except ImportError:
    command = None  # type: ignore[assignment]

from pycharter.config import set_database_url
from pycharter.db.config import (
    check_and_cleanup_invalid_alembic_version,
    detect_db_type,
    get_alembic_config,
    get_db_url,
    get_migrations_dir,
    require_alembic,
)
from pycharter.db.models.base import Base, get_engine
from pycharter.db.seed import cmd_seed

# ---- Init ----


def cmd_init(
    database_url: str | None = None,
    db_type: str | None = None,
    force: bool = False,
) -> int:
    """Initialize database schema (PostgreSQL, SQLite, or MongoDB)."""
    try:
        db_url = get_db_url(database_url)
        if not db_url:
            return 1
        db_type = db_type or detect_db_type(db_url)
        if db_type == "mongodb":
            return _init_mongodb(db_url, force)
        if db_type == "sqlite":
            return _init_sqlite(db_url, force)
        return _init_postgresql(db_url, force)
    except Exception as e:
        print(f"❌ Error: {e}")
        import traceback

        traceback.print_exc()
        return 1


def _init_mongodb(database_url: str, force: bool = False) -> int:
    """Initialize MongoDB (create indexes)."""
    try:
        from pycharter.metadata_store.mongodb import MongoDBMetadataStore

        database_name = "pycharter"
        if "/" in database_url.split("@")[-1]:
            db_part = database_url.split("/")[-1].split("?")[0]
            if db_part and db_part != database_url.split("@")[-1]:
                database_name = db_part
        print(f"Initializing MongoDB database: {database_name}")
        store = MongoDBMetadataStore(
            connection_string=database_url, database_name=database_name
        )
        store.connect(ensure_indexes=True)
        print("✓ MongoDB initialized successfully!")
        store.disconnect()
        return 0
    except ImportError:
        print("❌ Error: pymongo is required. Install with: pip install pymongo")
        return 1
    except Exception as e:
        print(f"❌ Error initializing MongoDB: {e}")
        import traceback

        traceback.print_exc()
        return 1


def _init_sqlite(database_url: str, force: bool = False) -> int:
    """Initialize SQLite: run migrations only (same approach as PostgreSQL)."""
    require_alembic()
    try:
        db_path = (
            database_url[10:] if database_url.startswith("sqlite:///") else database_url
        )
        if db_path != ":memory:":
            Path(db_path).parent.mkdir(parents=True, exist_ok=True)
        engine = get_engine(database_url)
        existing_tables = inspect(engine).get_table_names()
        if existing_tables and not force:
            print(
                f"⚠ Warning: Database already contains {len(existing_tables)} tables."
            )
            print(
                "   Use --force to reinitialize, or run 'pycharter db upgrade' to apply migrations."
            )
            return 1
        if existing_tables and force:
            with engine.connect() as conn:
                conn.execute(text("PRAGMA foreign_keys=OFF"))
                for name in existing_tables:
                    conn.execute(text(f'DROP TABLE IF EXISTS "{name}"'))
                conn.execute(text("PRAGMA foreign_keys=ON"))
                conn.commit()
            print("✓ Dropped existing tables")
        set_database_url(database_url)
        config = get_alembic_config(database_url)
        print("Running migrations to initialize database...")
        command.upgrade(config, "head")
        print("✓ SQLite initialization complete!")
        return 0
    except Exception as e:
        print(f"❌ Error initializing SQLite: {e}")
        import traceback

        traceback.print_exc()
        return 1


def _init_postgresql(database_url: str, force: bool = False) -> int:
    """Initialize PostgreSQL: create pycharter schema and run migrations."""
    require_alembic()
    try:
        set_database_url(database_url)
        config = get_alembic_config(database_url)
        engine = create_engine(database_url)
        with engine.connect() as conn:
            conn.execute(text('CREATE SCHEMA IF NOT EXISTS "pycharter"'))
            conn.commit()
        print("✓ Created 'pycharter' schema (if it didn't exist)")
        print("Running migrations to initialize database...")
        command.upgrade(config, "head")
        print("✓ Database initialized successfully!")
        return 0
    except Exception as e:
        print(f"❌ Error initializing database: {e}")
        import traceback

        traceback.print_exc()
        return 1


# ---- Migrations (upgrade / downgrade / current / stamp / history) ----


def cmd_upgrade(database_url: str | None = None, revision: str = "head") -> int:
    """Upgrade database to the given revision (default head)."""
    require_alembic()
    try:
        db_url = get_db_url(database_url)
        if not db_url:
            return 1
        if database_url:
            set_database_url(database_url)
        config = get_alembic_config(db_url)
        print(f"Upgrading database to revision: {revision}...")
        command.upgrade(config, revision)
        print("✓ Database upgraded successfully!")
        return 0
    except Exception as e:
        print(f"❌ Error upgrading database: {e}")
        import traceback

        traceback.print_exc()
        return 1


def cmd_downgrade(database_url: str | None = None, revision: str = "-1") -> int:
    """Downgrade database to the given revision."""
    require_alembic()
    try:
        db_url = get_db_url(database_url)
        if not db_url:
            return 1
        if database_url:
            set_database_url(database_url)
        config = get_alembic_config(db_url)
        print(f"Downgrading database to revision: {revision}...")
        command.downgrade(config, revision)
        print("✓ Database downgraded successfully!")
        return 0
    except Exception as e:
        print(f"❌ Error downgrading database: {e}")
        import traceback

        traceback.print_exc()
        return 1


def cmd_current(database_url: str | None = None) -> int:
    """Print current database revision."""
    require_alembic()
    try:
        db_url = get_db_url(database_url)
        if not db_url:
            return 1
        if database_url:
            set_database_url(database_url)
        engine = create_engine(db_url)
        with engine.connect() as connection:
            try:
                result = connection.execute(
                    text('SELECT version_num FROM "pycharter".alembic_version LIMIT 1')
                )
                version = result.fetchone()
                if version:
                    print(f"Current database revision: {version[0]}")
                    return 0
            except Exception:
                pass
            from alembic.runtime.migration import MigrationContext

            context = MigrationContext.configure(connection)
            current_rev = context.get_current_revision()
            if current_rev:
                print(f"Current database revision: {current_rev}")
                return 0
        print("Database is not initialized. Run 'pycharter db init' first.")
        return 1
    except Exception as e:
        print(f"❌ Error getting current revision: {e}")
        return 1


def cmd_stamp(database_url: str | None = None, revision: str = "head") -> int:
    """Stamp database with a revision without running migrations."""
    require_alembic()
    try:
        db_url = get_db_url(database_url)
        if not db_url:
            return 1
        if database_url:
            set_database_url(database_url)
        config = get_alembic_config(db_url)
        print(f"Stamping database with revision: {revision}...")
        command.stamp(config, revision)
        print(f"✓ Database stamped successfully with revision: {revision}")
        return 0
    except Exception as e:
        print(f"❌ Error stamping database: {e}")
        import traceback

        traceback.print_exc()
        return 1


def cmd_history(database_url: str | None = None) -> int:
    """Show migration history."""
    require_alembic()
    try:
        if database_url:
            set_database_url(database_url)
        config = get_alembic_config()
        command.history(config)
        return 0
    except Exception as e:
        print(f"❌ Error showing history: {e}")
        return 1


# ---- Truncate ----


def cmd_truncate(database_url: str | None = None, force: bool = False) -> int:
    """Truncate all PyCharter tables (PostgreSQL or SQLite). Prompts for confirmation unless --force."""
    require_alembic()
    try:
        db_url = get_db_url(database_url)
        if not db_url:
            return 1
        if database_url:
            set_database_url(database_url)
        engine = create_engine(db_url)
        inspector = inspect(engine)
        is_sqlite = detect_db_type(db_url) == "sqlite"
        existing_tables = set(
            inspector.get_table_names()
            if is_sqlite
            else inspector.get_table_names(schema="pycharter")
        )
        tables = [
            "metadata_record_business_owners",
            "metadata_record_bu_sme",
            "metadata_record_it_application_owners",
            "metadata_record_it_sme",
            "metadata_record_support_lead",
            "metadata_record_system_pulls",
            "metadata_record_system_pushes",
            "metadata_record_system_sources",
            "metadata_record_domains",
            "coercion_rules",
            "validation_rules",
            "schemas",
            "metadata_records",
            "data_contracts",
            "owners",
            "systems",
            "domains",
        ]
        existing_tables_to_truncate = [t for t in tables if t in existing_tables]
        if not existing_tables_to_truncate:
            print("⚠ No PyCharter tables found to truncate.")
            return 0
        print(
            f"\n⚠ WARNING: This will truncate {len(existing_tables_to_truncate)} table(s):"
        )
        print(f"   {', '.join(existing_tables_to_truncate)}")
        print("\n⚠ ALL DATA IN THESE TABLES WILL BE PERMANENTLY DELETED!")
        if not force:
            try:
                confirmation = input("\nType 'yes' to confirm: ").strip().lower()
                if confirmation not in ["yes", "y"]:
                    print("❌ Truncate cancelled.")
                    return 1
            except (EOFError, KeyboardInterrupt):
                print("\n❌ Truncate cancelled.")
                return 1
        with engine.begin() as conn:
            if is_sqlite:
                for table in existing_tables_to_truncate:
                    conn.execute(text(f'DELETE FROM "{table}"'))
                    print(f"  ✓ Truncated {table}")
            else:
                conn.execute(text("SET session_replication_role = 'replica'"))
                for table in existing_tables_to_truncate:
                    conn.execute(text(f'TRUNCATE TABLE pycharter."{table}" CASCADE'))
                    print(f"  ✓ Truncated {table}")
                conn.execute(text("SET session_replication_role = 'origin'"))
        print("\n✓ Successfully truncated all existing PyCharter tables!")
        return 0
    except Exception as e:
        print(f"❌ Error truncating database: {e}")
        import traceback

        traceback.print_exc()
        return 1


# ---- Entrypoint ----


def main() -> int:
    """Main CLI entry point for pycharter db commands."""
    parser = argparse.ArgumentParser(
        description="PyCharter database management commands",
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )
    subparsers = parser.add_subparsers(dest="command", help="Command to run")

    init_parser = subparsers.add_parser(
        "init", help="Initialize database schema from scratch"
    )
    init_parser.add_argument(
        "database_url",
        nargs="?",
        help="Database connection string (defaults to sqlite:///pycharter.db)",
    )
    init_parser.add_argument(
        "--db",
        "--database-type",
        dest="db_type",
        choices=["postgresql", "postgres", "mongodb", "sqlite"],
        default=None,
        help="Database type (auto-detected from URL if not provided)",
    )
    init_parser.add_argument(
        "--force", action="store_true", help="Proceed even if initialized"
    )

    upgrade_parser = subparsers.add_parser(
        "upgrade", help="Upgrade database to latest revision"
    )
    upgrade_parser.add_argument(
        "database_url", nargs="?", help="Database connection string"
    )
    upgrade_parser.add_argument("--revision", default="head", help="Target revision")

    downgrade_parser = subparsers.add_parser("downgrade", help="Downgrade database")
    downgrade_parser.add_argument(
        "database_url", nargs="?", help="Database connection string"
    )
    downgrade_parser.add_argument("--revision", default="-1", help="Target revision")

    current_parser = subparsers.add_parser(
        "current", help="Show current database revision"
    )
    current_parser.add_argument(
        "database_url", nargs="?", help="Database connection string"
    )

    history_parser = subparsers.add_parser("history", help="Show migration history")
    history_parser.add_argument(
        "database_url", nargs="?", help="Database connection string"
    )

    stamp_parser = subparsers.add_parser("stamp", help="Stamp database with revision")
    stamp_parser.add_argument(
        "revision", nargs="?", default="head", help="Revision to stamp"
    )
    stamp_parser.add_argument(
        "database_url", nargs="?", help="Database connection string"
    )

    seed_parser = subparsers.add_parser("seed", help="Seed database with initial data")
    seed_parser.add_argument(
        "seed_dir", nargs="?", help="Directory with seed YAML files"
    )
    seed_parser.add_argument(
        "database_url", nargs="?", help="Database connection string"
    )

    truncate_parser = subparsers.add_parser("truncate", help="Truncate all tables")
    truncate_parser.add_argument(
        "database_url", nargs="?", help="Database connection string"
    )
    truncate_parser.add_argument(
        "--force", action="store_true", help="Skip confirmation"
    )

    args = parser.parse_args()

    if not args.command:
        parser.print_help()
        return 1

    commands = {
        "init": lambda: cmd_init(
            args.database_url, db_type=args.db_type, force=args.force
        ),
        "upgrade": lambda: cmd_upgrade(args.database_url, args.revision),
        "downgrade": lambda: cmd_downgrade(args.database_url, args.revision),
        "current": lambda: cmd_current(args.database_url),
        "history": lambda: cmd_history(args.database_url),
        "stamp": lambda: cmd_stamp(args.database_url, args.revision),
        "seed": lambda: cmd_seed(args.seed_dir, args.database_url),
        "truncate": lambda: cmd_truncate(args.database_url, force=args.force),
    }
    return commands.get(args.command, lambda: (parser.print_help(), 1)[1])()


if __name__ == "__main__":
    sys.exit(main())
