from __future__ import annotations

from datetime import datetime
from typing import TypeVar

from sqlalchemy import MetaData, func
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
{% if uid in ("uuidv4", "uuidv7") %}
import uuid
from sqlalchemy import Uuid
{% elif uid == "nanoid" %}
from sqlalchemy import String
from nanoid import generate
{% elif uid == "ulid" %}
from sqlalchemy import String
from ulid import ULID
{% elif uid == "sparkid" %}
from sqlalchemy import String
from sparkid import generate_id
{% endif %}

__all__ = ("Base", "BaseTable", "ConcreteTable")

meta = MetaData(
    naming_convention={
        "ix": "ix_%(column_0_label)s",
        "uq": "uq_%(table_name)s_%(column_0_name)s",
        "ck": "ck_%(table_name)s_%(constraint_name)s",
        "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
        "pk": "pk_%(table_name)s",
    }
)


class Base(DeclarativeBase):
    metadata = meta


class TimestampMixin:
{% if uid == "uuidv4" %}
    id: Mapped[uuid.UUID] = mapped_column(Uuid(as_uuid=True), primary_key=True, default=uuid.uuid4)
{% elif uid == "uuidv7" %}
    id: Mapped[uuid.UUID] = mapped_column(Uuid(as_uuid=True), primary_key=True, default=uuid.uuid7)
{% elif uid == "nanoid" %}
    id: Mapped[str] = mapped_column(String(21), primary_key=True, default=generate)
{% elif uid == "ulid" %}
    id: Mapped[str] = mapped_column(String(26), primary_key=True, default=lambda: str(ULID()))
{% elif uid == "sparkid" %}
    id: Mapped[str] = mapped_column(String(21), primary_key=True, default=generate_id)
{% else %}
    id: Mapped[int] = mapped_column(primary_key=True)
{% endif %}
    created_at: Mapped[datetime] = mapped_column(
        default=datetime.utcnow,
        server_default=func.CURRENT_TIMESTAMP(),
    )
    updated_at: Mapped[datetime] = mapped_column(
        default=datetime.utcnow,
        onupdate=datetime.utcnow,
        server_default=func.CURRENT_TIMESTAMP(),
    )


class BaseTable(TimestampMixin, Base):
    __abstract__ = True


ConcreteTable = TypeVar("ConcreteTable", bound="BaseTable")
