from datetime import UTC, datetime
from enum import Enum
from typing import TypeVar

from pydantic import SecretStr, TypeAdapter
from sqlalchemy import JSON, DateTime, String, TypeDecorator
from sqlalchemy.orm import DeclarativeBase


class Base(DeclarativeBase):
    """
    Base class for all SQLAlchemy models.

    Uses SQLAlchemy 2.0 DeclarativeBase for proper type inference with Mapped types.
    This is backward compatible with existing Column() definitions while enabling
    gradual migration to mapped_column() with Mapped[T] type annotations.
    """

    pass


T = TypeVar('T', bound=Enum)


def create_json_type_decorator(object_type: type):
    """Create a decorator for a particular type. Introduced because SQLAlchemy could not process lists of enum values."""
    type_adapter: TypeAdapter = TypeAdapter(object_type)

    class JsonTypeDecorator(TypeDecorator):
        impl = JSON
        cache_ok = True

        def process_bind_param(self, value, dialect):
            return type_adapter.dump_python(
                value, mode='json', context={'expose_secrets': True}
            )

        def process_result_param(self, value, dialect):
            return type_adapter.validate_python(value)

    return JsonTypeDecorator


class StoredSecretStr(TypeDecorator):
    """TypeDecorator for secret strings. Encrypts the value using the default key before storing."""

    impl = String
    cache_ok = True

    def process_bind_param(self, value, dialect):
        if value is not None:
            from openhands.app_server.config import get_global_config

            jwt_service_injector = get_global_config().jwt
            assert jwt_service_injector is not None
            jwt_service = jwt_service_injector.get_jwt_service()
            token = jwt_service.create_jwe_token({'v': value.get_secret_value()})
            return token
        return None

    def process_result_param(self, value, dialect):
        if value is not None:
            from openhands.app_server.config import get_global_config

            jwt_service_injector = get_global_config().jwt
            assert jwt_service_injector is not None
            jwt_service = jwt_service_injector.get_jwt_service()
            token = jwt_service.decrypt_jwe_token(value)
            return SecretStr(token['v'])
        return None


class UtcDateTime(TypeDecorator):
    """TypeDecorator for datetime - stores all datetimes in utc. Assumes datetime without
    a specified timezone are utc. (Sqlite doesn't always return these)"""

    impl = DateTime(timezone=True)
    cache_ok = True

    def process_bind_param(self, value, dialect):
        if isinstance(value, datetime) and value.tzinfo != UTC:
            value = value.astimezone(UTC)
        return value

    def process_result_param(self, value, dialect):
        if isinstance(value, datetime):
            if value.tzinfo is None:
                value = value.replace(tzinfo=UTC)
            elif value.tzinfo != UTC:
                value = value.astimezone(UTC)
        return value


def create_enum_type_decorator(enum_type: type[T]):
    class EnumTypeDecorator(TypeDecorator):
        impl = String
        cache_ok = True

        def process_bind_param(self, value, dialect):
            if value is None:
                return None
            return value.value

        def process_result_param(self, value, dialect):
            if value:
                return enum_type[value]

    return EnumTypeDecorator


def row2dict(row):
    d = {}
    for column in row.__table__.columns:
        d[column.name] = getattr(row, column.name)

    return d
