import logging
from typing import List, Optional, TYPE_CHECKING
from datetime import datetime

from .migration import Migration

if TYPE_CHECKING:
    from ..core.db import Database

logger = logging.getLogger(__name__)


class Migrator:
    """Manages database migrations."""

    def __init__(self, db: "Database"):
        """
        Initialize the migrator.

        Args:
            db: Database instance
        """
        self.db = db
        self.migrations: List[Migration] = []
        self._ensure_migrations_table()

    def _ensure_migrations_table(self) -> None:
        """Create the migrations tracking table if it doesn't exist."""
        sql = """
        CREATE TABLE IF NOT EXISTS _migrations (
            version TEXT PRIMARY KEY,
            name TEXT NOT NULL,
            applied_at TIMESTAMP NOT NULL
        )
        """
        self.db.execute(sql)
        logger.info("Migrations table ready")

    def add_migration(self, migration: Migration) -> None:
        """
        Add a migration to the migrator.

        Args:
            migration: Migration to add
        """
        self.migrations.append(migration)
        self.migrations.sort()

    def get_applied_migrations(self) -> List[str]:
        """Get list of applied migration versions."""
        result = self.db.execute("SELECT version FROM _migrations ORDER BY version")
        return [row[0] for row in result.fetchall()]

    def get_pending_migrations(self) -> List[Migration]:
        """Get list of pending migrations."""
        applied = set(self.get_applied_migrations())
        return [m for m in self.migrations if m.version not in applied]

    def migrate(self, target: Optional[str] = None) -> None:
        """
        Apply pending migrations up to target version.

        Args:
            target: Target version to migrate to. If None, apply all pending migrations.
        """
        pending = self.get_pending_migrations()

        if not pending:
            logger.info("No pending migrations")
            return

        migrations_to_apply = pending
        if target:
            migrations_to_apply = [m for m in pending if m.version <= target]

        for migration in migrations_to_apply:
            try:
                with self.db.transaction():
                    migration.apply(self.db)
                    # Record the migration
                    self.db.execute(
                        "INSERT INTO _migrations (version, name, applied_at) VALUES (?, ?, ?)",
                        [migration.version, migration.name, datetime.now()],
                    )
                logger.info(f"Successfully applied migration: {migration.version}")
            except Exception as e:
                logger.error(f"Failed to apply migration {migration.version}: {e}")
                raise

    def rollback(self, steps: int = 1) -> None:
        """
        Rollback the last N migrations.

        Args:
            steps: Number of migrations to rollback
        """
        applied = self.get_applied_migrations()

        if not applied:
            logger.info("No migrations to rollback")
            return

        # Get the migrations to rollback (most recent first)
        to_rollback = applied[-steps:]

        for version in reversed(to_rollback):
            migration = next((m for m in self.migrations if m.version == version), None)

            if not migration:
                logger.warning(f"Migration {version} not found in migration list")
                continue

            try:
                with self.db.transaction():
                    migration.rollback(self.db)
                    # Remove the migration record
                    self.db.execute(
                        "DELETE FROM _migrations WHERE version = ?", [version]
                    )
                logger.info(f"Successfully rolled back migration: {version}")
            except Exception as e:
                logger.error(f"Failed to rollback migration {version}: {e}")
                raise

    def status(self) -> None:
        """Print the status of all migrations."""
        applied = set(self.get_applied_migrations())

        print("\nMigration Status:")
        print("-" * 60)

        if not self.migrations:
            print("No migrations found")
            return

        for migration in self.migrations:
            status = "✓ Applied" if migration.version in applied else "○ Pending"
            print(f"{status}  {migration.version}_{migration.name}")

        pending_count = len([m for m in self.migrations if m.version not in applied])
        print(f"\n{len(applied)} applied, {pending_count} pending")
