"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""

from __future__ import annotations
from datetime import datetime
from pydantic import model_serializer
from tofupilot.v2.types import (
    BaseModel,
    Nullable,
    OptionalNullable,
    UNSET,
    UNSET_SENTINEL,
)
from tofupilot.v2.utils import FieldMetadata, QueryParamMetadata
from typing import List, Literal, Optional
from typing_extensions import Annotated, NotRequired, TypedDict


QueryParamOutcome = Literal["PASS", "FAIL", "ERROR", "TIMEOUT", "ABORTED"]

RunListSortBy = Literal["started_at", "created_at", "duration"]
r"""Field to sort results by."""

RunListSortOrder = Literal["asc", "desc"]
r"""Sort order direction."""


class RunListRequestTypedDict(TypedDict):
    search_query: NotRequired[str]
    ids: NotRequired[List[str]]
    outcomes: NotRequired[List[QueryParamOutcome]]
    procedure_ids: NotRequired[List[str]]
    procedure_versions: NotRequired[List[str]]
    serial_numbers: NotRequired[List[str]]
    part_numbers: NotRequired[List[str]]
    revision_numbers: NotRequired[List[str]]
    duration_min: NotRequired[str]
    duration_max: NotRequired[str]
    started_after: NotRequired[datetime]
    started_before: NotRequired[datetime]
    ended_after: NotRequired[datetime]
    ended_before: NotRequired[datetime]
    created_after: NotRequired[datetime]
    created_before: NotRequired[datetime]
    created_by_user_ids: NotRequired[List[str]]
    created_by_station_ids: NotRequired[List[str]]
    operated_by_ids: NotRequired[List[str]]
    limit: NotRequired[int]
    cursor: NotRequired[float]
    sort_by: NotRequired[RunListSortBy]
    r"""Field to sort results by."""
    sort_order: NotRequired[RunListSortOrder]
    r"""Sort order direction."""


class RunListRequest(BaseModel):
    search_query: Annotated[
        Optional[str],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = None

    ids: Annotated[
        Optional[List[str]],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = None

    outcomes: Annotated[
        Optional[List[QueryParamOutcome]],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = None

    procedure_ids: Annotated[
        Optional[List[str]],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = None

    procedure_versions: Annotated[
        Optional[List[str]],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = None

    serial_numbers: Annotated[
        Optional[List[str]],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = None

    part_numbers: Annotated[
        Optional[List[str]],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = None

    revision_numbers: Annotated[
        Optional[List[str]],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = None

    duration_min: Annotated[
        Optional[str],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = None

    duration_max: Annotated[
        Optional[str],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = None

    started_after: Annotated[
        Optional[datetime],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = None

    started_before: Annotated[
        Optional[datetime],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = None

    ended_after: Annotated[
        Optional[datetime],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = None

    ended_before: Annotated[
        Optional[datetime],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = None

    created_after: Annotated[
        Optional[datetime],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = None

    created_before: Annotated[
        Optional[datetime],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = None

    created_by_user_ids: Annotated[
        Optional[List[str]],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = None

    created_by_station_ids: Annotated[
        Optional[List[str]],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = None

    operated_by_ids: Annotated[
        Optional[List[str]],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = None

    limit: Annotated[
        Optional[int],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = 50

    cursor: Annotated[
        Optional[float],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = None

    sort_by: Annotated[
        Optional[RunListSortBy],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = "started_at"
    r"""Field to sort results by."""

    sort_order: Annotated[
        Optional[RunListSortOrder],
        FieldMetadata(query=QueryParamMetadata(style="form", explode=True)),
    ] = "desc"
    r"""Sort order direction."""


RunListDataOutcome = Literal["PASS", "FAIL", "ERROR", "TIMEOUT", "ABORTED"]
r"""Final result of the run execution."""


class RunListCreatedByUserTypedDict(TypedDict):
    r"""User whose API key was used to create this run. Only returned if `all` or `created_by` is included."""

    id: str
    r"""User ID."""
    name: Nullable[str]
    r"""User display name."""
    email: Nullable[str]
    r"""User email address."""
    image: Nullable[str]
    r"""User profile image URL."""


class RunListCreatedByUser(BaseModel):
    r"""User whose API key was used to create this run. Only returned if `all` or `created_by` is included."""

    id: str
    r"""User ID."""

    name: Nullable[str]
    r"""User display name."""

    email: Nullable[str]
    r"""User email address."""

    image: Nullable[str]
    r"""User profile image URL."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = []
        nullable_fields = ["name", "email", "image"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in type(self).model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class RunListCreatedByStationTypedDict(TypedDict):
    r"""Station whose API key was used to create this run. Only returned if `all` or `created_by` is included."""

    id: str
    r"""Station ID."""
    name: str
    r"""Station name."""
    image: Nullable[str]
    r"""Station image URL."""


class RunListCreatedByStation(BaseModel):
    r"""Station whose API key was used to create this run. Only returned if `all` or `created_by` is included."""

    id: str
    r"""Station ID."""

    name: str
    r"""Station name."""

    image: Nullable[str]
    r"""Station image URL."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = []
        nullable_fields = ["image"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in type(self).model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class RunListOperatedByTypedDict(TypedDict):
    r"""User who operated this run. Only returned if `all` or `operated_by` is included."""

    id: str
    r"""Operator ID."""
    name: Nullable[str]
    r"""Operator display name."""
    email: Nullable[str]
    r"""Operator email address."""
    image: Nullable[str]
    r"""Operator profile image URL."""


class RunListOperatedBy(BaseModel):
    r"""User who operated this run. Only returned if `all` or `operated_by` is included."""

    id: str
    r"""Operator ID."""

    name: Nullable[str]
    r"""Operator display name."""

    email: Nullable[str]
    r"""Operator email address."""

    image: Nullable[str]
    r"""Operator profile image URL."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = []
        nullable_fields = ["name", "email", "image"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in type(self).model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class RunListVersionTypedDict(TypedDict):
    r"""Version of the procedure used for this run."""

    id: str
    r"""Procedure version ID."""
    tag: str
    r"""Version tag."""


class RunListVersion(BaseModel):
    r"""Version of the procedure used for this run."""

    id: str
    r"""Procedure version ID."""

    tag: str
    r"""Version tag."""


class RunListProcedureTypedDict(TypedDict):
    r"""Test procedure associated with this run."""

    id: str
    r"""Procedure ID."""
    name: str
    r"""Procedure name."""
    version: Nullable[RunListVersionTypedDict]
    r"""Version of the procedure used for this run."""


class RunListProcedure(BaseModel):
    r"""Test procedure associated with this run."""

    id: str
    r"""Procedure ID."""

    name: str
    r"""Procedure name."""

    version: Nullable[RunListVersion]
    r"""Version of the procedure used for this run."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = []
        nullable_fields = ["version"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in type(self).model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class RunListRevisionTypedDict(TypedDict):
    r"""Revision information for this unit."""

    id: str
    r"""Revision ID."""
    number: str
    r"""Revision number."""
    image: Nullable[str]
    r"""Revision image URL."""


class RunListRevision(BaseModel):
    r"""Revision information for this unit."""

    id: str
    r"""Revision ID."""

    number: str
    r"""Revision number."""

    image: Nullable[str]
    r"""Revision image URL."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = []
        nullable_fields = ["image"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in type(self).model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class RunListPartTypedDict(TypedDict):
    r"""Part information with revision details."""

    id: str
    r"""Part ID."""
    number: str
    r"""Part number."""
    name: str
    r"""Part name."""
    revision: RunListRevisionTypedDict
    r"""Revision information for this unit."""


class RunListPart(BaseModel):
    r"""Part information with revision details."""

    id: str
    r"""Part ID."""

    number: str
    r"""Part number."""

    name: str
    r"""Part name."""

    revision: RunListRevision
    r"""Revision information for this unit."""


class RunListBatchTypedDict(TypedDict):
    r"""Batch information for this unit."""

    id: str
    r"""Batch ID."""
    number: str
    r"""Batch number."""


class RunListBatch(BaseModel):
    r"""Batch information for this unit."""

    id: str
    r"""Batch ID."""

    number: str
    r"""Batch number."""


class RunListUnitTypedDict(TypedDict):
    r"""Unit under test information."""

    id: str
    r"""Unit ID."""
    serial_number: str
    r"""Unit serial number."""
    part: RunListPartTypedDict
    r"""Part information with revision details."""
    batch: NotRequired[Nullable[RunListBatchTypedDict]]
    r"""Batch information for this unit."""


class RunListUnit(BaseModel):
    r"""Unit under test information."""

    id: str
    r"""Unit ID."""

    serial_number: str
    r"""Unit serial number."""

    part: RunListPart
    r"""Part information with revision details."""

    batch: OptionalNullable[RunListBatch] = UNSET
    r"""Batch information for this unit."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = ["batch"]
        nullable_fields = ["batch"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in type(self).model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class RunListDataTypedDict(TypedDict):
    id: str
    r"""Unique identifier for the run."""
    created_at: datetime
    r"""ISO 8601 timestamp when the run was created."""
    started_at: datetime
    r"""ISO 8601 timestamp when the run execution started."""
    ended_at: datetime
    r"""ISO 8601 timestamp when the run execution ended."""
    duration: str
    r"""ISO 8601 duration string representing the total execution time."""
    outcome: RunListDataOutcome
    r"""Final result of the run execution."""
    procedure: RunListProcedureTypedDict
    r"""Test procedure associated with this run."""
    unit: RunListUnitTypedDict
    r"""Unit under test information."""
    docstring: NotRequired[Nullable[str]]
    r"""Additional notes or documentation about this test run."""
    created_by_user: NotRequired[Nullable[RunListCreatedByUserTypedDict]]
    r"""User whose API key was used to create this run. Only returned if `all` or `created_by` is included."""
    created_by_station: NotRequired[Nullable[RunListCreatedByStationTypedDict]]
    r"""Station whose API key was used to create this run. Only returned if `all` or `created_by` is included."""
    operated_by: NotRequired[Nullable[RunListOperatedByTypedDict]]
    r"""User who operated this run. Only returned if `all` or `operated_by` is included."""


class RunListData(BaseModel):
    id: str
    r"""Unique identifier for the run."""

    created_at: datetime
    r"""ISO 8601 timestamp when the run was created."""

    started_at: datetime
    r"""ISO 8601 timestamp when the run execution started."""

    ended_at: datetime
    r"""ISO 8601 timestamp when the run execution ended."""

    duration: str
    r"""ISO 8601 duration string representing the total execution time."""

    outcome: RunListDataOutcome
    r"""Final result of the run execution."""

    procedure: RunListProcedure
    r"""Test procedure associated with this run."""

    unit: RunListUnit
    r"""Unit under test information."""

    docstring: OptionalNullable[str] = UNSET
    r"""Additional notes or documentation about this test run."""

    created_by_user: OptionalNullable[RunListCreatedByUser] = UNSET
    r"""User whose API key was used to create this run. Only returned if `all` or `created_by` is included."""

    created_by_station: OptionalNullable[RunListCreatedByStation] = UNSET
    r"""Station whose API key was used to create this run. Only returned if `all` or `created_by` is included."""

    operated_by: OptionalNullable[RunListOperatedBy] = UNSET
    r"""User who operated this run. Only returned if `all` or `operated_by` is included."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = [
            "docstring",
            "created_by_user",
            "created_by_station",
            "operated_by",
        ]
        nullable_fields = [
            "docstring",
            "created_by_user",
            "created_by_station",
            "operated_by",
        ]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in type(self).model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class RunListMetaTypedDict(TypedDict):
    has_more: bool
    r"""Whether there are more results available for pagination."""
    next_cursor: Nullable[float]
    r"""Cursor value to fetch the next page of results. Use this value as the cursor parameter in the next request. Null if no more results available."""


class RunListMeta(BaseModel):
    has_more: bool
    r"""Whether there are more results available for pagination."""

    next_cursor: Nullable[float]
    r"""Cursor value to fetch the next page of results. Use this value as the cursor parameter in the next request. Null if no more results available."""

    @model_serializer(mode="wrap")
    def serialize_model(self, handler):
        optional_fields = []
        nullable_fields = ["next_cursor"]
        null_default_fields = []

        serialized = handler(self)

        m = {}

        for n, f in type(self).model_fields.items():
            k = f.alias or n
            val = serialized.get(k)
            serialized.pop(k, None)

            optional_nullable = k in optional_fields and k in nullable_fields
            is_set = (
                self.__pydantic_fields_set__.intersection({n})
                or k in null_default_fields
            )  # pylint: disable=no-member

            if val is not None and val != UNSET_SENTINEL:
                m[k] = val
            elif val != UNSET_SENTINEL and (
                not k in optional_fields or (optional_nullable and is_set)
            ):
                m[k] = val

        return m


class RunListResponseTypedDict(TypedDict):
    r"""Paginated list of test runs matching the filter criteria."""

    data: List[RunListDataTypedDict]
    meta: RunListMetaTypedDict


class RunListResponse(BaseModel):
    r"""Paginated list of test runs matching the filter criteria."""

    data: List[RunListData]

    meta: RunListMeta
