import logging
from typing import Dict, Any, TYPE_CHECKING
from dataclasses import dataclass

if TYPE_CHECKING:
    from ..core.db import Database

logger = logging.getLogger(__name__)


@dataclass
class ColumnInfo:
    """Represents column information."""

    name: str
    type: str
    nullable: bool = True
    unique: bool = False
    primary_key: bool = False


@dataclass
class TableInfo:
    """Represents table information."""

    name: str
    columns: Dict[str, ColumnInfo]


class SchemaDiff:
    """Detects differences between model definitions and database schema."""

    def __init__(self, db: "Database"):
        """
        Initialize schema diff.

        Args:
            db: Database instance
        """
        self.db = db

    def get_database_schema(self) -> Dict[str, TableInfo]:
        """
        Get current database schema.

        Returns:
            Dictionary of table name to TableInfo
        """
        schema: Dict[str, TableInfo] = {}

        # Get all tables using backend-specific implementation
        tables = self.db.backend.get_tables(exclude_migrations=True)

        # Get column info for each table
        for table in tables:
            raw_columns = self.db.backend.get_table_columns(table)
            columns = {}

            # Convert raw column info to ColumnInfo objects
            for col in raw_columns:
                col_name = col[1]
                col_type = col[2]
                is_nullable = not col[3]  # notnull = 1 means NOT NULL
                is_primary = col[5] == 1

                columns[col_name] = ColumnInfo(
                    name=col_name,
                    type=col_type,
                    nullable=is_nullable,
                    primary_key=is_primary,
                )

            schema[table] = TableInfo(name=table, columns=columns)

        return schema

    def get_model_schema(self) -> Dict[str, TableInfo]:
        """
        Get schema from model definitions.

        Returns:
            Dictionary of table name to TableInfo
        """
        from ..orm.models import ModelMeta

        schema: Dict[str, TableInfo] = {}

        for model_cls in ModelMeta.models:
            table_name = model_cls._table_name
            columns: Dict[str, ColumnInfo] = {}

            for field_name, field in model_cls._fields.items():
                col_name = field.get_column_name()
                col_type = self.db.backend.get_field_type(field)

                columns[col_name] = ColumnInfo(
                    name=col_name,
                    type=col_type,
                    nullable=field.nullable,
                    unique=field.unique,
                    primary_key=(field_name == "id"),
                )

            schema[table_name] = TableInfo(name=table_name, columns=columns)

        return schema

    def diff(self) -> Dict[str, Any]:
        """
        Compare model schema with database schema.

        Returns:
            Dictionary containing differences:
            - tables_to_create: List of table names to create
            - tables_to_drop: List of table names to drop
            - columns_to_add: Dict of table -> list of columns to add
            - columns_to_drop: Dict of table -> list of columns to drop
            - columns_to_modify: Dict of table -> list of columns with type changes
        """
        db_schema = self.get_database_schema()
        model_schema = self.get_model_schema()

        db_tables = set(db_schema.keys())
        model_tables = set(model_schema.keys())

        diff_result = {
            "tables_to_create": list(model_tables - db_tables),
            "tables_to_drop": list(db_tables - model_tables),
            "columns_to_add": {},
            "columns_to_drop": {},
            "columns_to_modify": {},
        }

        # Check for column differences in common tables
        common_tables = db_tables & model_tables

        for table in common_tables:
            db_cols = set(db_schema[table].columns.keys())
            model_cols = set(model_schema[table].columns.keys())

            # Columns to add
            cols_to_add = model_cols - db_cols
            if cols_to_add:
                diff_result["columns_to_add"][table] = [
                    model_schema[table].columns[col] for col in cols_to_add
                ]

            # Columns to drop
            cols_to_drop = db_cols - model_cols
            if cols_to_drop:
                diff_result["columns_to_drop"][table] = list(cols_to_drop)

            # Columns with type changes
            for col in db_cols & model_cols:
                db_col = db_schema[table].columns[col]
                model_col = model_schema[table].columns[col]

                # Simple type comparison (could be enhanced)
                if db_col.type.upper() != model_col.type.upper():
                    if table not in diff_result["columns_to_modify"]:
                        diff_result["columns_to_modify"][table] = []
                    diff_result["columns_to_modify"][table].append(
                        {"column": col, "from": db_col.type, "to": model_col.type}
                    )

        return diff_result

    def print_diff(self) -> None:
        """Print the schema differences in a readable format."""
        diff = self.diff()

        print("\n" + "=" * 60)
        print("Schema Diff")
        print("=" * 60)

        has_changes = False

        if diff["tables_to_create"]:
            has_changes = True
            print("\n📦 Tables to Create:")
            for table in diff["tables_to_create"]:
                print(f"  + {table}")

        if diff["tables_to_drop"]:
            has_changes = True
            print("\n🗑️  Tables to Drop:")
            for table in diff["tables_to_drop"]:
                print(f"  - {table}")

        if diff["columns_to_add"]:
            has_changes = True
            print("\n➕ Columns to Add:")
            for table, columns in diff["columns_to_add"].items():
                print(f"  {table}:")
                for col in columns:
                    print(f"    + {col.name} ({col.type})")

        if diff["columns_to_drop"]:
            has_changes = True
            print("\n➖ Columns to Drop:")
            for table, columns in diff["columns_to_drop"].items():
                print(f"  {table}:")
                for col in columns:
                    print(f"    - {col}")

        if diff["columns_to_modify"]:
            has_changes = True
            print("\n🔄 Columns to Modify:")
            for table, changes in diff["columns_to_modify"].items():
                print(f"  {table}:")
                for change in changes:
                    print(
                        f"    ~ {change['column']}: {change['from']} → {change['to']}"
                    )

        if not has_changes:
            print("\n✓ No schema changes detected")

        print("\n" + "=" * 60)
