from collections.abc import Mapping, Sequence
from typing import Any

from pydantic import (
    AwareDatetime,
    BaseModel,
    ConfigDict,
    Field,
    computed_field,
    field_serializer,
)

from qualibrate.core.models.node_status import ElementRunStatus
from qualibrate.core.models.outcome import Outcome
from qualibrate.core.models.run_summary.run_error import RunError
from qualibrate.core.parameters import NodeParameters, RunnableParameters
from qualibrate.core.utils.type_protocols import TargetType

__all__ = [
    "ExecutionHistory",
    "ExecutionHistoryItem",
    "ItemData",
    "ItemMetadata",
]


class ItemMetadata(BaseModel):
    model_config = ConfigDict(extra="allow")

    name: str
    status: ElementRunStatus
    description: str | None = None
    run_start: AwareDatetime
    run_end: AwareDatetime

    @computed_field
    def run_duration(self) -> float:
        return round((self.run_end - self.run_start).total_seconds(), 3)


class ItemData(BaseModel):
    model_config = ConfigDict(extra="allow")

    quam: dict[str, Any] | None = None
    parameters: RunnableParameters
    outcomes: dict[TargetType, Outcome] = Field(default_factory=dict)
    error: RunError | None = None

    @field_serializer("parameters")
    def serialize_parameters(self, parameters: NodeParameters) -> Mapping[str, Any]:
        return parameters.model_dump(serialize_as_any=True)


class ExecutionHistoryItem(BaseModel):
    """Represents an item of graph execution history."""

    model_config = ConfigDict()

    id: int | None = None
    created_at: AwareDatetime
    metadata: ItemMetadata
    data: ItemData

    elements_history: "ExecutionHistory | None" = None


class ExecutionHistory(BaseModel):
    """Represents a graph execution history."""

    items: Sequence[ExecutionHistoryItem]
