"""Schema migration system for .dss archive compatibility"""

from typing import Dict, Any, List, Callable
import json


class SchemaMigration:
    """Base class for schema migrations"""

    source_version: str = "1.0.0"
    target_version: str = "1.0.1"

    def up(self, data: Dict[str, Any]) -> Dict[str, Any]:
        """Migrate data from source to target version"""
        raise NotImplementedError

    def down(self, data: Dict[str, Any]) -> Dict[str, Any]:
        """Rollback migration"""
        raise NotImplementedError


class MigrationV1_0_0_to_V1_0_1(SchemaMigration):
    """Initial migration: add UUID support to all entities"""

    source_version = "1.0.0"
    target_version = "1.0.1"

    def up(self, data: Dict[str, Any]) -> Dict[str, Any]:
        """Add UUID fields if missing"""
        from uuid import uuid4

        # Ensure all entities have UUIDs (backward compat)
        if 'project' in data:
            if 'uuid' not in data['project']:
                data['project']['uuid'] = str(uuid4())

        if 'tokens' in data:
            for token_name, token in data['tokens'].items():
                if isinstance(token, dict) and 'uuid' not in token:
                    token['uuid'] = str(uuid4())

        if 'components' in data:
            for comp in data['components']:
                if 'uuid' not in comp:
                    comp['uuid'] = str(uuid4())
                if 'variants' in comp:
                    for variant in comp['variants']:
                        if 'uuid' not in variant:
                            variant['uuid'] = str(uuid4())

        if 'themes' in data:
            for theme in data['themes']:
                if 'uuid' not in theme:
                    theme['uuid'] = str(uuid4())

        return data

    def down(self, data: Dict[str, Any]) -> Dict[str, Any]:
        """Remove UUID fields (rollback)"""
        if 'project' in data:
            data['project'].pop('uuid', None)

        if 'tokens' in data:
            for token in data['tokens'].values():
                if isinstance(token, dict):
                    token.pop('uuid', None)

        if 'components' in data:
            for comp in data['components']:
                comp.pop('uuid', None)
                if 'variants' in comp:
                    for variant in comp['variants']:
                        variant.pop('uuid', None)

        if 'themes' in data:
            for theme in data['themes']:
                theme.pop('uuid', None)

        return data


class MigrationManager:
    """Manages schema migrations for .dss archives"""

    # Map of version pairs to migration classes
    MIGRATIONS: Dict[tuple, type] = {
        ("1.0.0", "1.0.1"): MigrationV1_0_0_to_V1_0_1,
    }

    # Ordered list of all schema versions
    VERSIONS = ["1.0.0", "1.0.1"]

    @classmethod
    def migrate(cls, data: Dict[str, Any], from_version: str, to_version: str) -> Dict[str, Any]:
        """
        Apply migrations from source version to target version.

        Args:
            data: Archive data to migrate
            from_version: Current schema version in archive
            to_version: Target schema version

        Returns:
            Migrated data

        Raises:
            ValueError: If migration path doesn't exist or versions are invalid
        """
        if from_version == to_version:
            return data

        # Validate versions
        if from_version not in cls.VERSIONS:
            raise ValueError(f"Unknown source schema version: {from_version}")
        if to_version not in cls.VERSIONS:
            raise ValueError(f"Unknown target schema version: {to_version}")

        from_idx = cls.VERSIONS.index(from_version)
        to_idx = cls.VERSIONS.index(to_version)

        if from_idx > to_idx:
            raise ValueError(f"Cannot downgrade from {from_version} to {to_version}")

        # Apply migrations sequentially
        current_version = from_version
        while current_version != to_version:
            current_idx = cls.VERSIONS.index(current_version)
            next_version = cls.VERSIONS[current_idx + 1]

            migration_key = (current_version, next_version)
            if migration_key not in cls.MIGRATIONS:
                raise ValueError(f"No migration found for {current_version} -> {next_version}")

            migration_class = cls.MIGRATIONS[migration_key]
            migration = migration_class()
            data = migration.up(data)
            current_version = next_version

        return data

    @classmethod
    def get_latest_version(cls) -> str:
        """Get latest schema version"""
        return cls.VERSIONS[-1]


# Export
__all__ = ['MigrationManager', 'SchemaMigration']
