import os
import re
from datetime import datetime
from pathlib import Path
from typing import Optional

from dbwarden.config import get_config
from dbwarden.engine.file_parser import parse_upgrade_statements
from dbwarden.engine.model_discovery import (
    get_all_model_tables,
    auto_discover_model_paths,
    generate_create_table_sql,
    generate_drop_table_sql,
    generate_add_column_sql,
    extract_tables_from_migrations,
    ModelTable,
)
from dbwarden.engine.version import (
    get_migrations_directory,
    get_next_migration_number,
)
from dbwarden.logging import get_logger


def get_pending_migration_statements(migrations_dir: str) -> set[str]:
    """
    Get all SQL statements from all migration files (for deduplication).

    Args:
        migrations_dir: Path to migrations directory.

    Returns:
        Set of normalized SQL statements from all migration files.
    """
    all_statements = set()

    if not os.path.exists(migrations_dir):
        return all_statements

    for filename in os.listdir(migrations_dir):
        if not filename.endswith(".sql"):
            continue
        filepath = os.path.join(migrations_dir, filename)
        statements = parse_upgrade_statements(filepath)
        for stmt in statements:
            normalized = stmt.strip()
            if normalized:
                all_statements.add(normalized)

    return all_statements


def make_migrations_cmd(
    description: str | None = None,
    verbose: bool = False,
) -> None:
    """
    Auto-generate SQL migration from SQLAlchemy models.

    Args:
        description: Description for the migration.
        verbose: Enable verbose logging.
    """
    logger = get_logger(verbose=verbose)

    logger.log_execution_mode("async" if is_async_enabled() else "sync")

    config = get_config()
    model_paths = config.model_paths

    if model_paths is None:
        model_paths = auto_discover_model_paths()

    if not model_paths:
        logger.warning("No model paths found. Please set model_paths in warden.toml")
        print("No SQLAlchemy models found. Please:")
        print("  1. Create models/ directory with your SQLAlchemy models")
        print("  2. Or set model_paths in warden.toml")
        return

    logger.info(f"Discovering models in: {model_paths}")
    tables = get_all_model_tables(model_paths)

    if not tables:
        logger.warning("No tables found in models")
        print("No tables found in the specified model paths.")
        return

    logger.info(f"Found {len(tables)} tables in models")

    migrations_dir = get_migrations_directory()
    next_number = get_next_migration_number(migrations_dir)
    safe_desc = re.sub(r"[^a-zA-Z0-9]", "_", description or "auto_generated").lower()
    filename = f"{next_number}_{safe_desc}.sql"

    upgrade_sql, rollback_sql = generate_migration_sql(tables, migrations_dir)

    if not upgrade_sql.strip():
        print(
            "No new migrations to generate - all models already covered by existing migrations."
        )
        return

    filepath = os.path.join(migrations_dir, filename)

    content = f"""-- upgrade

{upgrade_sql}

-- rollback

{rollback_sql}
"""

    with open(filepath, "w") as f:
        f.write(content)

    logger.info(f"Created migration file: {filename}")
    print(f"Created migration file: {filepath}")
    print(f"Tables included: {', '.join(t.name for t in tables)}")


def generate_migration_sql(
    tables: list, migrations_dir: str | None = None
) -> tuple[str, str]:
    """
    Generate upgrade and rollback SQL from table definitions.

    Compares model tables with existing migrations to generate:
    - CREATE TABLE for new tables
    - ALTER TABLE ADD COLUMN for new columns in existing tables

    Args:
        tables: List of ModelTable objects.
        migrations_dir: Path to migrations directory for deduplication.

    Returns:
        Tuple of (upgrade_sql, rollback_sql).
    """
    existing_tables = {}
    if migrations_dir:
        existing_tables = extract_tables_from_migrations(migrations_dir)

    upgrade_parts = []
    rollback_parts = []

    for table in tables:
        existing_columns = existing_tables.get(table.name, set())

        if not existing_columns:
            create_sql = generate_create_table_sql(table)
            upgrade_parts.append(create_sql)
            rollback_parts.append(generate_drop_table_sql(table.name))
        else:
            for column in table.columns:
                if column.name.lower() not in existing_columns:
                    alter_sql = generate_add_column_sql(table.name, column)
                    upgrade_parts.append(alter_sql)
                    rollback_parts.append(
                        f"ALTER TABLE {table.name} DROP COLUMN {column.name}"
                    )

    rollback_parts.reverse()

    return "\n\n".join(upgrade_parts), "\n\n".join(rollback_parts)


def new_migration_cmd(description: str, version: str | None = None) -> None:
    """
    Create a new manual migration file.

    Args:
        description: Description of the migration.
        version: Version number for the migration.
    """
    logger = get_logger()

    migrations_dir = get_migrations_directory()

    if version is None:
        version = get_next_migration_number(migrations_dir)

    safe_description = re.sub(r"[^a-zA-Z0-9]", "_", description).lower()
    filename = f"{version}_{safe_description}.sql"
    filepath = os.path.join(migrations_dir, filename)

    content = f"""-- upgrade

-- {description}

-- rollback

-- {description}
"""

    with open(filepath, "w") as f:
        f.write(content)

    logger.info(f"Created migration file: {filename}")
    print(f"Created migration file: {filepath}")


def is_async_enabled() -> bool:
    """Check if async mode is enabled."""
    from dbwarden.database.connection import is_async_enabled

    return is_async_enabled()
