"""ORM 基类与通用字段。

刻意保持"贫血模型"：这些类只管持久化，不放业务逻辑。
业务逻辑在 engine/（纯函数）与 services/（编排），这样才能不起数据库单测。
"""
from __future__ import annotations

import datetime as _dt
import uuid
from typing import Any, Dict

from sqlalchemy import DateTime, JSON, String
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column


class Base(DeclarativeBase):
    """所有表的基类。

    type_annotation_map 里只用方言中立的类型：SQLite（开发）与 PostgreSQL（生产）
    行为一致。不要引入 JSONB / ARRAY / UUID 这些 PG 特有类型 —— 见 ADR-0001。
    """

    type_annotation_map = {
        Dict[str, Any]: JSON,
        dict: JSON,
        list: JSON,
    }


def utcnow() -> _dt.datetime:
    """统一的当前时间：naive UTC。

    SQLite 不保存时区，所以全仓库统一存 naive UTC，序列化时再补 Z。
    禁止在任何地方用 datetime.now()（本地时区）—— 实验室里跨时区协作会出事。
    """
    return _dt.datetime.utcnow()


def new_id(prefix: str) -> str:
    """带前缀的 ID，如 run_7f3a9c2e...。

    前缀让日志和时间线一眼可读 —— 排障时不用回表查这个 ID 是什么东西。
    """
    return "{0}_{1}".format(prefix, uuid.uuid4().hex[:20])


def iso_z(value: Any) -> Any:
    """naive UTC datetime → ISO8601 带 Z。非 datetime 原样返回。"""
    if isinstance(value, _dt.datetime):
        return value.replace(microsecond=value.microsecond).isoformat() + "Z"
    return value


class TimestampMixin:
    created_at: Mapped[_dt.datetime] = mapped_column(DateTime, default=utcnow, nullable=False)
    updated_at: Mapped[_dt.datetime] = mapped_column(
        DateTime, default=utcnow, onupdate=utcnow, nullable=False
    )


def pk(prefix: str) -> Mapped[str]:
    return mapped_column(
        String(32), primary_key=True, default=lambda: new_id(prefix)
    )
