"""任务元数据: Session 上层,落 PG `tasks` 表(§7 B Step 3)。

Session 只管对话消息;Task 管 mode/description/status/model/tokens/cost/时间戳
—— 跨轮次共享的元数据,DESIGN.md §7.1 / §7.4 规约。

state.json 已废除;字段从 PG 读出,save() 走 INSERT ... ON CONFLICT DO UPDATE。
created_at / updated_at 由 PG server_default / onupdate 管,Python 侧只读。
"""
from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime
from typing import Optional
from uuid import UUID

from .storage import upsert_task
from .storage.models import Task as TaskRow
from .storage.utils import get_task


def _iso(dt: Optional[datetime]) -> str:
    return dt.isoformat(timespec="seconds") if dt else ""


@dataclass
class TaskState:
    task_id: str                 # UUID 字符串形式(对外展示用,DB 仍是 UUID)
    user_id: UUID                # 归属 user,UPSERT INSERT 路径必填(列 NOT NULL)
    name: str = ""               # 任务显示名(列 NOT NULL,新建必填;resume 时从 DB 读)
    working_dir: str = ""        # 工作目录(db 形态:ROOT 内相对 / ROOT 外绝对;空=未绑)
    skill: str = ""              # 智能体类型(coding / ppt / proposal / 自由形式,后续可对齐 skills/ 注册表)
    description: str = ""        # 一句话描述,便于列表识别
    status: str = "active"       # active / completed / abandoned
    model: str = ""              # caps.model_id
    model_profile: str = ""      # 档案名,如 deepseek_v4.flash
    reasoning_effort: str = ""
    tokens_prompt: int = 0
    tokens_completion: int = 0
    cost_cny: float = 0.0
    created_at: str = ""         # PG server_default 填,Python 侧只读
    updated_at: str = ""

    @property
    def tokens_total(self) -> int:
        return self.tokens_prompt + self.tokens_completion

    def save(self) -> None:
        """UPSERT 到 PG。created_at / updated_at 不参与写入(PG 自动管)。"""
        upsert_task(
            UUID(self.task_id),
            user_id=self.user_id,
            name=self.name,
            working_dir=self.working_dir,
            skill=self.skill,
            description=self.description,
            status=self.status,
            model=self.model,
            model_profile=self.model_profile,
            reasoning_effort=self.reasoning_effort,
            tokens_prompt=self.tokens_prompt,
            tokens_completion=self.tokens_completion,
        )

    @classmethod
    def from_row(cls, row: TaskRow) -> "TaskState":
        return cls(
            task_id=str(row.task_id),
            user_id=row.user_id,
            name=row.name,
            working_dir=row.working_dir,
            skill=row.skill,
            description=row.description,
            status=row.status,
            model=row.model,
            model_profile=row.model_profile,
            reasoning_effort=row.reasoning_effort,
            tokens_prompt=row.tokens_prompt,
            tokens_completion=row.tokens_completion,
            cost_cny=float(row.cost_cny or 0),
            created_at=_iso(row.created_at),
            updated_at=_iso(row.updated_at),
        )

    @classmethod
    def load(cls, task_id: UUID) -> Optional["TaskState"]:
        """从 PG 读;不存在返回 None。"""
        row = get_task(task_id)
        return cls.from_row(row) if row is not None else None
