from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Literal, Optional

from flownet.core.types import PayloadData


ExceptionAction = Literal["propagate", "abort", "fallback"]


@dataclass
class ExceptionContext:
    request_id: str
    phase: str
    actor_id: Optional[str] = None
    method: Optional[str] = None
    address: Optional[str] = None
    transformation_id: Optional[str] = None
    payload: Optional[PayloadData] = None
    stack_depth: int = 0
    metadata: dict[str, Any] = field(default_factory=dict)


@dataclass
class ExceptionEvent:
    error_type: str
    message: str
    traceback: str
    context: ExceptionContext
    error: Optional[BaseException] = None


@dataclass
class ExceptionDecision:
    action: ExceptionAction
    payloads: Optional[list[PayloadData]] = None
    metadata: dict[str, Any] = field(default_factory=dict)

    @staticmethod
    def abort(**metadata: Any) -> "ExceptionDecision":
        return ExceptionDecision(action="abort", payloads=None, metadata=metadata)

    @staticmethod
    def propagate(**metadata: Any) -> "ExceptionDecision":
        return ExceptionDecision(action="propagate", payloads=None, metadata=metadata)

    @staticmethod
    def fallback(
        value: Optional[Any] = None,
        payloads: Optional[list[PayloadData]] = None,
        **metadata: Any,
    ) -> "ExceptionDecision":
        resolved_payloads: Optional[list[PayloadData]]
        if payloads is not None:
            resolved_payloads = payloads
        elif value is None:
            resolved_payloads = []
        elif isinstance(value, PayloadData):
            resolved_payloads = [value]
        else:
            resolved_payloads = [PayloadData(args=[value], kwargs={})]
        return ExceptionDecision(action="fallback", payloads=resolved_payloads, metadata=metadata)


class FlowException(RuntimeError):
    def __init__(self, event: ExceptionEvent):
        message = f"{event.error_type}: {event.message}"
        super().__init__(message)
        self.event = event


class FlowDefinitionError(RuntimeError):
    """Raised when a flow definition violates return/sink semantics."""
