from typing import Optional
from uuid import UUID

from sqlalchemy import ForeignKey, String
from sqlalchemy.orm import Mapped, mapped_column

from letta.orm.base import Base


def is_valid_uuid4(uuid_string: str) -> bool:
    """Check if a string is a valid UUID4."""
    try:
        uuid_obj = UUID(uuid_string)
        return uuid_obj.version == 4
    except ValueError:
        return False


class OrganizationMixin(Base):
    """Mixin for models that belong to an organization."""

    __abstract__ = True

    organization_id: Mapped[str] = mapped_column(String, ForeignKey("organizations.id"))


class UserMixin(Base):
    """Mixin for models that belong to a user."""

    __abstract__ = True

    user_id: Mapped[str] = mapped_column(String, ForeignKey("users.id"))


class AgentMixin(Base):
    """Mixin for models that belong to an agent."""

    __abstract__ = True

    agent_id: Mapped[str] = mapped_column(String, ForeignKey("agents.id", ondelete="CASCADE"))


class FileMixin(Base):
    """Mixin for models that belong to a file."""

    __abstract__ = True

    file_id: Mapped[Optional[str]] = mapped_column(String, ForeignKey("files.id", ondelete="CASCADE"))


class SourceMixin(Base):
    """Mixin for models (e.g. file) that belong to a source."""

    __abstract__ = True

    source_id: Mapped[str] = mapped_column(String, ForeignKey("sources.id", ondelete="CASCADE"), nullable=False)


class SandboxConfigMixin(Base):
    """Mixin for models that belong to a SandboxConfig."""

    __abstract__ = True

    sandbox_config_id: Mapped[str] = mapped_column(String, ForeignKey("sandbox_configs.id"))


class ProjectMixin(Base):
    """Mixin for models that belong to a project."""

    __abstract__ = True

    project_id: Mapped[str] = mapped_column(String, nullable=True, doc="The associated project id.")


class ArchiveMixin(Base):
    """Mixin for models that belong to an archive."""

    __abstract__ = True

    archive_id: Mapped[str] = mapped_column(String, ForeignKey("archives.id", ondelete="CASCADE"))


class TemplateMixin(Base):
    """TemplateMixin for models that belong to a template."""

    __abstract__ = True

    base_template_id: Mapped[str] = mapped_column(nullable=True, doc="The id of the base template.")
    template_id: Mapped[str] = mapped_column(nullable=True, doc="The id of the template.")
    deployment_id: Mapped[str] = mapped_column(nullable=True, doc="The id of the deployment.")


class TemplateEntityMixin(Base):
    """Mixin for models that belong to an entity (only used for templates)."""

    __abstract__ = True

    entity_id: Mapped[str] = mapped_column(nullable=True, doc="The id of the entity within the template.")
