"""Run 事件唯一写入口，同时维护当前状态投影。"""
from __future__ import annotations

from typing import Any, Dict, Optional

from sqlalchemy.ext.asyncio import AsyncSession

from ..engine.states import assert_run_transition
from ..enums import EventSource, Severity
from ..models import Run, RunEvent, utcnow


async def append_event(
    session: AsyncSession,
    run: Run,
    kind: str,
    message: str,
    *,
    source: str = EventSource.CONTROL_PLANE.value,
    severity: str = Severity.INFO.value,
    payload: Optional[Dict[str, Any]] = None,
    target_state: Optional[str] = None,
    projection: Optional[Dict[str, Any]] = None,
    idempotency_key: Optional[str] = None,
    step_id: Optional[str] = None,
    loop_index: Optional[int] = None,
) -> RunEvent:
    if target_state is not None and target_state != run.state:
        assert_run_transition(run.state, target_state)

    run.event_seq += 1
    event = RunEvent(
        run_id=run.id,
        seq=run.event_seq,
        ts=utcnow(),
        source=source,
        kind=kind,
        severity=severity,
        message=message,
        payload=payload or {},
        idempotency_key=idempotency_key,
        step_id=step_id,
        loop_index=loop_index,
    )
    session.add(event)

    if target_state is not None:
        run.state = target_state
    for key, value in (projection or {}).items():
        if not hasattr(run, key):
            raise AttributeError("Run 不存在投影字段: {0}".format(key))
        setattr(run, key, value)
    await session.flush()
    return event
