import json
from collections.abc import Mapping
from datetime import datetime
from typing import Any, cast
from uuid import uuid4

import sqlalchemy as sa
from sqlalchemy import DateTime, String, func
from sqlalchemy.orm import Mapped, mapped_column

from .base import TypeBase
from .types import LongText, StringUUID


class SandboxProviderSystemConfig(TypeBase):
    """
    System-level sandbox provider configuration.
    Stores default configuration for each provider type.
    """

    __tablename__ = "sandbox_provider_system_config"
    __table_args__ = (
        sa.PrimaryKeyConstraint("id", name="sandbox_provider_system_config_pkey"),
        sa.UniqueConstraint("provider_type", name="unique_sandbox_provider_system_config_type"),
    )

    id: Mapped[str] = mapped_column(
        StringUUID, insert_default=lambda: str(uuid4()), default_factory=lambda: str(uuid4()), init=False
    )
    provider_type: Mapped[str] = mapped_column(String(50), nullable=False, comment="e2b, docker, local, ssh")
    encrypted_config: Mapped[str] = mapped_column(LongText, nullable=False, comment="Encrypted config JSON")
    created_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, server_default=func.current_timestamp(), init=False
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime,
        nullable=False,
        server_default=func.current_timestamp(),
        server_onupdate=func.current_timestamp(),
        init=False,
    )


class SandboxProvider(TypeBase):
    """
    Tenant-level sandbox provider configuration.
    Each tenant can have one configuration per provider type.
    Only one provider can be active at a time per tenant.
    """

    __tablename__ = "sandbox_providers"
    __table_args__ = (
        sa.PrimaryKeyConstraint("id", name="sandbox_provider_pkey"),
        sa.UniqueConstraint("tenant_id", "provider_type", "configure_type", name="unique_sandbox_provider_tenant_type"),
        sa.Index("idx_sandbox_providers_tenant_id", "tenant_id"),
        sa.Index("idx_sandbox_providers_tenant_active", "tenant_id", "is_active"),
    )

    id: Mapped[str] = mapped_column(
        StringUUID, insert_default=lambda: str(uuid4()), default_factory=lambda: str(uuid4()), init=False
    )
    tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    provider_type: Mapped[str] = mapped_column(String(50), nullable=False, comment="e2b, docker, local, ssh")
    encrypted_config: Mapped[str] = mapped_column(LongText, nullable=False, comment="Encrypted config JSON")
    configure_type: Mapped[str] = mapped_column(String(20), nullable=False, server_default="user", default="user")
    is_active: Mapped[bool] = mapped_column(sa.Boolean, nullable=False, server_default=sa.text("false"), default=False)
    created_at: Mapped[datetime] = mapped_column(
        DateTime, nullable=False, server_default=func.current_timestamp(), init=False
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime,
        nullable=False,
        server_default=func.current_timestamp(),
        server_onupdate=func.current_timestamp(),
        init=False,
    )

    @property
    def config(self) -> Mapping[str, Any]:
        return cast(Mapping[str, Any], json.loads(self.encrypted_config or "{}"))
