from __future__ import annotations

import enum
from datetime import datetime
from typing import Any

from sqlalchemy import JSON, Boolean, DateTime, Enum, ForeignKey, Integer, String, Text, func
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship


class Base(DeclarativeBase):
    pass


class Job(Base):
    __tablename__ = "jobs"

    id: Mapped[str] = mapped_column(String, primary_key=True)
    cron_expr: Mapped[str] = mapped_column(String, nullable=False)
    handler_path: Mapped[str] = mapped_column(String, nullable=False)
    public_cfg: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict, nullable=False)
    # 密文 token（Fernet 加密后的字符串）
    secret_cfg: Mapped[str] = mapped_column(Text, default="", nullable=False)

    enabled: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
    last_run_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
    created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False
    )

    logs: Mapped[list["JobLog"]] = relationship(back_populates="job", cascade="all, delete-orphan")


class JobStatus(str, enum.Enum):
    SUCCESS = "SUCCESS"
    FAILURE = "FAILURE"
    RETRY = "RETRY"


class JobLog(Base):
    __tablename__ = "job_logs"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    job_id: Mapped[str] = mapped_column(ForeignKey("jobs.id"), index=True, nullable=False)

    status: Mapped[JobStatus] = mapped_column(
        Enum(JobStatus, native_enum=False, length=16),
        nullable=False,
    )
    snapshot_params: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict, nullable=False)

    message: Mapped[str] = mapped_column(Text, default="", nullable=False)
    traceback: Mapped[str] = mapped_column(Text, default="", nullable=False)
    # 本次执行期间捕获到的完整运行日志（可能很长，按上层捕获器做截断）
    run_log: Mapped[str] = mapped_column(Text, default="", nullable=False)
    celery_task_id: Mapped[str] = mapped_column(String, default="", nullable=False)
    attempt: Mapped[int] = mapped_column(Integer, default=0, nullable=False)

    started_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), nullable=False)
    finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)

    job: Mapped[Job] = relationship(back_populates="logs")


