from __future__ import annotations

import logging
from dataclasses import dataclass, field
from typing import Any, Generic, TypeVar
from uuid import uuid4

from .errors import WorkflowExecutionError
from .history import HistoryRecorder
from .models import Event, INTERNAL_START_EVENT

StateT = TypeVar("StateT")


@dataclass
class WorkflowContext(Generic[StateT]):
    run_id: str
    workflow_name: str
    task_name: str
    event: Event
    attempt: int
    state: StateT
    history: HistoryRecorder
    logger: logging.Logger
    _outbox: list[Event] = field(default_factory=list)
    _stop_requested: bool = False
    _stop_result: Any = None

    def emit(
        self,
        name: str,
        payload: Any = None,
        metadata: dict[str, Any] | None = None,
    ) -> None:
        if not isinstance(name, str) or not name:
            raise WorkflowExecutionError("Event name must be a non-empty string")
        if name == INTERNAL_START_EVENT:
            raise WorkflowExecutionError(
                f"{INTERNAL_START_EVENT} is reserved for internal workflow use"
            )
        if self._stop_requested:
            raise WorkflowExecutionError("Cannot emit events after stop() was requested")

        self._outbox.append(
            Event(
                event_id=uuid4().hex,
                name=name,
                payload=payload,
                source_task=self.task_name,
                source_event_id=self.event.event_id,
                metadata=metadata or {},
            )
        )

    def stop(self, result: Any = None) -> None:
        self._stop_requested = True
        self._stop_result = result

    def log(self, message: str, **fields) -> None:
        details = {"message": message, **fields}
        self.history.record(
            "task_log",
            task_name=self.task_name,
            event_id=self.event.event_id,
            event_name=self.event.name,
            attempt=self.attempt,
            details=details,
        )
        self.logger.info("%s", message, extra={"workflow": fields})

    @property
    def outbox(self) -> list[Event]:
        return list(self._outbox)

    @property
    def stop_requested(self) -> bool:
        return self._stop_requested

    @property
    def stop_result(self) -> Any:
        return self._stop_result
