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

from .basesdk import BaseSDK
from datetime import datetime
from tofupilot.v2 import errors, models, utils
from tofupilot.v2._hooks import HookContext
from tofupilot.v2.types import OptionalNullable, UNSET
from tofupilot.v2.utils import get_security_from_env
from typing import Any, List, Mapping, Optional, Union


class Runs(BaseSDK):
    def list(
        self,
        *,
        search_query: Optional[str] = None,
        ids: Optional[List[str]] = None,
        outcomes: Optional[List[models.QueryParamOutcome]] = None,
        procedure_ids: Optional[List[str]] = None,
        procedure_versions: Optional[List[str]] = None,
        serial_numbers: Optional[List[str]] = None,
        part_numbers: Optional[List[str]] = None,
        revision_numbers: Optional[List[str]] = None,
        duration_min: Optional[str] = None,
        duration_max: Optional[str] = None,
        started_after: Optional[datetime] = None,
        started_before: Optional[datetime] = None,
        ended_after: Optional[datetime] = None,
        ended_before: Optional[datetime] = None,
        created_after: Optional[datetime] = None,
        created_before: Optional[datetime] = None,
        created_by_user_ids: Optional[List[str]] = None,
        created_by_station_ids: Optional[List[str]] = None,
        operated_by_ids: Optional[List[str]] = None,
        limit: Optional[int] = 50,
        cursor: Optional[float] = None,
        sort_by: Optional[models.RunListSortBy] = "started_at",
        sort_order: Optional[models.RunListSortOrder] = "desc",
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.RunListResponse:
        r"""List and filter runs

        Retrieve a paginated list of test runs with optional filtering by unit, procedure, date range, outcome, and other criteria. Supports sorting and includes run metadata.

        :param search_query:
        :param ids:
        :param outcomes:
        :param procedure_ids:
        :param procedure_versions:
        :param serial_numbers:
        :param part_numbers:
        :param revision_numbers:
        :param duration_min:
        :param duration_max:
        :param started_after:
        :param started_before:
        :param ended_after:
        :param ended_before:
        :param created_after:
        :param created_before:
        :param created_by_user_ids:
        :param created_by_station_ids:
        :param operated_by_ids:
        :param limit:
        :param cursor:
        :param sort_by: Field to sort results by.
        :param sort_order: Sort order direction.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.RunListRequest(
            search_query=search_query,
            ids=ids,
            outcomes=outcomes,
            procedure_ids=procedure_ids,
            procedure_versions=procedure_versions,
            serial_numbers=serial_numbers,
            part_numbers=part_numbers,
            revision_numbers=revision_numbers,
            duration_min=duration_min,
            duration_max=duration_max,
            started_after=started_after,
            started_before=started_before,
            ended_after=ended_after,
            ended_before=ended_before,
            created_after=created_after,
            created_before=created_before,
            created_by_user_ids=created_by_user_ids,
            created_by_station_ids=created_by_station_ids,
            operated_by_ids=operated_by_ids,
            limit=limit,
            cursor=cursor,
            sort_by=sort_by,
            sort_order=sort_order,
        )

        req = self._build_request(
            method="GET",
            path="/v2/runs",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="run-list",
                oauth2_scopes=[],
                security_source=get_security_from_env(
                    self.sdk_configuration.security, models.Security
                ),
            ),
            request=req,
            error_status_codes=["4XX", "500", "5XX"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return utils.unmarshal_json(http_res.text, models.RunListResponse)
        if utils.match_response(http_res, "500", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ERRORINTERNALSERVERERRORData
            )
            raise errors.ERRORINTERNALSERVERERROR(data=response_data)
        if utils.match_response(http_res, "4XX", "*"):
            http_res_text = utils.stream_to_text(http_res)
            raise errors.APIError(
                "API error occurred", http_res.status_code, http_res_text, http_res
            )
        if utils.match_response(http_res, "5XX", "*"):
            http_res_text = utils.stream_to_text(http_res)
            raise errors.APIError(
                "API error occurred", http_res.status_code, http_res_text, http_res
            )

        content_type = http_res.headers.get("Content-Type")
        http_res_text = utils.stream_to_text(http_res)
        raise errors.APIError(
            f"Unexpected response received (code: {http_res.status_code}, type: {content_type})",
            http_res.status_code,
            http_res_text,
            http_res,
        )

    async def list_async(
        self,
        *,
        search_query: Optional[str] = None,
        ids: Optional[List[str]] = None,
        outcomes: Optional[List[models.QueryParamOutcome]] = None,
        procedure_ids: Optional[List[str]] = None,
        procedure_versions: Optional[List[str]] = None,
        serial_numbers: Optional[List[str]] = None,
        part_numbers: Optional[List[str]] = None,
        revision_numbers: Optional[List[str]] = None,
        duration_min: Optional[str] = None,
        duration_max: Optional[str] = None,
        started_after: Optional[datetime] = None,
        started_before: Optional[datetime] = None,
        ended_after: Optional[datetime] = None,
        ended_before: Optional[datetime] = None,
        created_after: Optional[datetime] = None,
        created_before: Optional[datetime] = None,
        created_by_user_ids: Optional[List[str]] = None,
        created_by_station_ids: Optional[List[str]] = None,
        operated_by_ids: Optional[List[str]] = None,
        limit: Optional[int] = 50,
        cursor: Optional[float] = None,
        sort_by: Optional[models.RunListSortBy] = "started_at",
        sort_order: Optional[models.RunListSortOrder] = "desc",
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.RunListResponse:
        r"""List and filter runs

        Retrieve a paginated list of test runs with optional filtering by unit, procedure, date range, outcome, and other criteria. Supports sorting and includes run metadata.

        :param search_query:
        :param ids:
        :param outcomes:
        :param procedure_ids:
        :param procedure_versions:
        :param serial_numbers:
        :param part_numbers:
        :param revision_numbers:
        :param duration_min:
        :param duration_max:
        :param started_after:
        :param started_before:
        :param ended_after:
        :param ended_before:
        :param created_after:
        :param created_before:
        :param created_by_user_ids:
        :param created_by_station_ids:
        :param operated_by_ids:
        :param limit:
        :param cursor:
        :param sort_by: Field to sort results by.
        :param sort_order: Sort order direction.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.RunListRequest(
            search_query=search_query,
            ids=ids,
            outcomes=outcomes,
            procedure_ids=procedure_ids,
            procedure_versions=procedure_versions,
            serial_numbers=serial_numbers,
            part_numbers=part_numbers,
            revision_numbers=revision_numbers,
            duration_min=duration_min,
            duration_max=duration_max,
            started_after=started_after,
            started_before=started_before,
            ended_after=ended_after,
            ended_before=ended_before,
            created_after=created_after,
            created_before=created_before,
            created_by_user_ids=created_by_user_ids,
            created_by_station_ids=created_by_station_ids,
            operated_by_ids=operated_by_ids,
            limit=limit,
            cursor=cursor,
            sort_by=sort_by,
            sort_order=sort_order,
        )

        req = self._build_request_async(
            method="GET",
            path="/v2/runs",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="run-list",
                oauth2_scopes=[],
                security_source=get_security_from_env(
                    self.sdk_configuration.security, models.Security
                ),
            ),
            request=req,
            error_status_codes=["4XX", "500", "5XX"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return utils.unmarshal_json(http_res.text, models.RunListResponse)
        if utils.match_response(http_res, "500", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ERRORINTERNALSERVERERRORData
            )
            raise errors.ERRORINTERNALSERVERERROR(data=response_data)
        if utils.match_response(http_res, "4XX", "*"):
            http_res_text = await utils.stream_to_text_async(http_res)
            raise errors.APIError(
                "API error occurred", http_res.status_code, http_res_text, http_res
            )
        if utils.match_response(http_res, "5XX", "*"):
            http_res_text = await utils.stream_to_text_async(http_res)
            raise errors.APIError(
                "API error occurred", http_res.status_code, http_res_text, http_res
            )

        content_type = http_res.headers.get("Content-Type")
        http_res_text = await utils.stream_to_text_async(http_res)
        raise errors.APIError(
            f"Unexpected response received (code: {http_res.status_code}, type: {content_type})",
            http_res.status_code,
            http_res_text,
            http_res,
        )

    def create(
        self,
        *,
        outcome: models.RunCreateOutcome,
        procedure_id: str,
        started_at: datetime,
        ended_at: datetime,
        serial_number: str,
        procedure_version: OptionalNullable[str] = UNSET,
        operated_by: Optional[str] = None,
        part_number: Optional[str] = None,
        revision_number: Optional[str] = None,
        batch_number: Optional[str] = None,
        sub_units: Optional[List[str]] = None,
        docstring: Optional[str] = None,
        phases: Optional[
            Union[List[models.RunCreatePhase], List[models.RunCreatePhaseTypedDict]]
        ] = None,
        logs: Optional[
            Union[List[models.RunCreateLog], List[models.RunCreateLogTypedDict]]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.RunCreateResponse:
        r"""Create run

        Create a new test run linking to a procedure and unit. Unit identifiers are matched case-insensitively - existing entities are reused, new ones are created automatically.

        :param outcome: Overall test result. Use PASS when test succeeds, FAIL when test fails but script execution completed successfully, ERROR when script execution fails, TIMEOUT when test exceeds time limit, ABORTED for manual script interruption.
        :param procedure_id: Procedure ID. Create the procedure in the app first, then find the auto-generated ID on the procedure page.
        :param started_at: ISO 8601 timestamp when the test run began execution. This timestamp will be used to track when the test execution started and for historical analysis of test runs. A separate created_at timestamp is stored internally server side to track upload date.
        :param ended_at: ISO 8601 timestamp when the test run finished execution.
        :param serial_number: Unique serial number of the unit under test. Matched case-insensitively. If no unit with this serial number exists, one will be created.
        :param procedure_version: Specific version of the test procedure used for the run. Matched case-insensitively. If none exist, a procedure with this procedure version will be created. If no procedure version is specified, the run will not be linked to any specific version.
        :param operated_by: Email address of the operator who executed the test run. The operator must exist as a user in the system. The run will be linked to this user to track who performed the test.
        :param part_number: Component part number for the unit. Matched case-insensitively. This field is required if the part number cannot be extracted from the serial number (as set in the settings). This field takes precedence over extraction from serial number. A component with the provided or extracted part number will be created if one does not exist.
        :param revision_number: Hardware revision identifier for the unit. Matched case-insensitively. If none exist, a revision with this number will be created. If no revision is specified, the unit will be linked to the default revision of the part number.
        :param batch_number: Production batch identifier for grouping units manufactured together. Matched case-insensitively. If none exist, a batch with this batch number will be created. If no batch number is specified, the unit will not be linked to any batch.
        :param sub_units: Array of sub-unit serial numbers that are part of this main unit. Matched case-insensitively. Each sub-unit must already exist and will be linked as a sub-component of the main unit under test. If no sub-units are specified, the unit will be created without sub-unit relationships.
        :param docstring: Additional notes or documentation about this test run.
        :param phases: Array of test phases with measurements and results. Each phase represents a distinct stage of the test execution with timing information, outcome status, and optional measurements. If no phases are specified, the run will be created without phase-level organization of test data.
        :param logs: Array of log messages generated during the test execution. Each log entry captures events, errors, and diagnostic information with severity levels and source code references. If no logs are specified, the run will be created without log entries.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.RunCreateRequest(
            outcome=outcome,
            procedure_id=procedure_id,
            procedure_version=procedure_version,
            operated_by=operated_by,
            started_at=started_at,
            ended_at=ended_at,
            serial_number=serial_number,
            part_number=part_number,
            revision_number=revision_number,
            batch_number=batch_number,
            sub_units=sub_units,
            docstring=docstring,
            phases=utils.get_pydantic_model(
                phases, Optional[List[models.RunCreatePhase]]
            ),
            logs=utils.get_pydantic_model(logs, Optional[List[models.RunCreateLog]]),
        )

        req = self._build_request(
            method="POST",
            path="/v2/runs",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request, False, False, "json", models.RunCreateRequest
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="run-create",
                oauth2_scopes=[],
                security_source=get_security_from_env(
                    self.sdk_configuration.security, models.Security
                ),
            ),
            request=req,
            error_status_codes=["400", "403", "404", "422", "4XX", "500", "5XX"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return utils.unmarshal_json(http_res.text, models.RunCreateResponse)
        if utils.match_response(http_res, "400", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ErrorBADREQUESTData
            )
            raise errors.ErrorBADREQUEST(data=response_data)
        if utils.match_response(http_res, "403", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ErrorFORBIDDENData
            )
            raise errors.ErrorFORBIDDEN(data=response_data)
        if utils.match_response(http_res, "404", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ErrorNOTFOUNDData
            )
            raise errors.ErrorNOTFOUND(data=response_data)
        if utils.match_response(http_res, "422", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ErrorUNPROCESSABLECONTENTData
            )
            raise errors.ErrorUNPROCESSABLECONTENT(data=response_data)
        if utils.match_response(http_res, "500", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ERRORINTERNALSERVERERRORData
            )
            raise errors.ERRORINTERNALSERVERERROR(data=response_data)
        if utils.match_response(http_res, "4XX", "*"):
            http_res_text = utils.stream_to_text(http_res)
            raise errors.APIError(
                "API error occurred", http_res.status_code, http_res_text, http_res
            )
        if utils.match_response(http_res, "5XX", "*"):
            http_res_text = utils.stream_to_text(http_res)
            raise errors.APIError(
                "API error occurred", http_res.status_code, http_res_text, http_res
            )

        content_type = http_res.headers.get("Content-Type")
        http_res_text = utils.stream_to_text(http_res)
        raise errors.APIError(
            f"Unexpected response received (code: {http_res.status_code}, type: {content_type})",
            http_res.status_code,
            http_res_text,
            http_res,
        )

    async def create_async(
        self,
        *,
        outcome: models.RunCreateOutcome,
        procedure_id: str,
        started_at: datetime,
        ended_at: datetime,
        serial_number: str,
        procedure_version: OptionalNullable[str] = UNSET,
        operated_by: Optional[str] = None,
        part_number: Optional[str] = None,
        revision_number: Optional[str] = None,
        batch_number: Optional[str] = None,
        sub_units: Optional[List[str]] = None,
        docstring: Optional[str] = None,
        phases: Optional[
            Union[List[models.RunCreatePhase], List[models.RunCreatePhaseTypedDict]]
        ] = None,
        logs: Optional[
            Union[List[models.RunCreateLog], List[models.RunCreateLogTypedDict]]
        ] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.RunCreateResponse:
        r"""Create run

        Create a new test run linking to a procedure and unit. Unit identifiers are matched case-insensitively - existing entities are reused, new ones are created automatically.

        :param outcome: Overall test result. Use PASS when test succeeds, FAIL when test fails but script execution completed successfully, ERROR when script execution fails, TIMEOUT when test exceeds time limit, ABORTED for manual script interruption.
        :param procedure_id: Procedure ID. Create the procedure in the app first, then find the auto-generated ID on the procedure page.
        :param started_at: ISO 8601 timestamp when the test run began execution. This timestamp will be used to track when the test execution started and for historical analysis of test runs. A separate created_at timestamp is stored internally server side to track upload date.
        :param ended_at: ISO 8601 timestamp when the test run finished execution.
        :param serial_number: Unique serial number of the unit under test. Matched case-insensitively. If no unit with this serial number exists, one will be created.
        :param procedure_version: Specific version of the test procedure used for the run. Matched case-insensitively. If none exist, a procedure with this procedure version will be created. If no procedure version is specified, the run will not be linked to any specific version.
        :param operated_by: Email address of the operator who executed the test run. The operator must exist as a user in the system. The run will be linked to this user to track who performed the test.
        :param part_number: Component part number for the unit. Matched case-insensitively. This field is required if the part number cannot be extracted from the serial number (as set in the settings). This field takes precedence over extraction from serial number. A component with the provided or extracted part number will be created if one does not exist.
        :param revision_number: Hardware revision identifier for the unit. Matched case-insensitively. If none exist, a revision with this number will be created. If no revision is specified, the unit will be linked to the default revision of the part number.
        :param batch_number: Production batch identifier for grouping units manufactured together. Matched case-insensitively. If none exist, a batch with this batch number will be created. If no batch number is specified, the unit will not be linked to any batch.
        :param sub_units: Array of sub-unit serial numbers that are part of this main unit. Matched case-insensitively. Each sub-unit must already exist and will be linked as a sub-component of the main unit under test. If no sub-units are specified, the unit will be created without sub-unit relationships.
        :param docstring: Additional notes or documentation about this test run.
        :param phases: Array of test phases with measurements and results. Each phase represents a distinct stage of the test execution with timing information, outcome status, and optional measurements. If no phases are specified, the run will be created without phase-level organization of test data.
        :param logs: Array of log messages generated during the test execution. Each log entry captures events, errors, and diagnostic information with severity levels and source code references. If no logs are specified, the run will be created without log entries.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.RunCreateRequest(
            outcome=outcome,
            procedure_id=procedure_id,
            procedure_version=procedure_version,
            operated_by=operated_by,
            started_at=started_at,
            ended_at=ended_at,
            serial_number=serial_number,
            part_number=part_number,
            revision_number=revision_number,
            batch_number=batch_number,
            sub_units=sub_units,
            docstring=docstring,
            phases=utils.get_pydantic_model(
                phases, Optional[List[models.RunCreatePhase]]
            ),
            logs=utils.get_pydantic_model(logs, Optional[List[models.RunCreateLog]]),
        )

        req = self._build_request_async(
            method="POST",
            path="/v2/runs",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request, False, False, "json", models.RunCreateRequest
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="run-create",
                oauth2_scopes=[],
                security_source=get_security_from_env(
                    self.sdk_configuration.security, models.Security
                ),
            ),
            request=req,
            error_status_codes=["400", "403", "404", "422", "4XX", "500", "5XX"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return utils.unmarshal_json(http_res.text, models.RunCreateResponse)
        if utils.match_response(http_res, "400", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ErrorBADREQUESTData
            )
            raise errors.ErrorBADREQUEST(data=response_data)
        if utils.match_response(http_res, "403", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ErrorFORBIDDENData
            )
            raise errors.ErrorFORBIDDEN(data=response_data)
        if utils.match_response(http_res, "404", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ErrorNOTFOUNDData
            )
            raise errors.ErrorNOTFOUND(data=response_data)
        if utils.match_response(http_res, "422", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ErrorUNPROCESSABLECONTENTData
            )
            raise errors.ErrorUNPROCESSABLECONTENT(data=response_data)
        if utils.match_response(http_res, "500", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ERRORINTERNALSERVERERRORData
            )
            raise errors.ERRORINTERNALSERVERERROR(data=response_data)
        if utils.match_response(http_res, "4XX", "*"):
            http_res_text = await utils.stream_to_text_async(http_res)
            raise errors.APIError(
                "API error occurred", http_res.status_code, http_res_text, http_res
            )
        if utils.match_response(http_res, "5XX", "*"):
            http_res_text = await utils.stream_to_text_async(http_res)
            raise errors.APIError(
                "API error occurred", http_res.status_code, http_res_text, http_res
            )

        content_type = http_res.headers.get("Content-Type")
        http_res_text = await utils.stream_to_text_async(http_res)
        raise errors.APIError(
            f"Unexpected response received (code: {http_res.status_code}, type: {content_type})",
            http_res.status_code,
            http_res_text,
            http_res,
        )

    def delete(
        self,
        *,
        ids: List[str],
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.RunDeleteResponse:
        r"""Delete runs

        Permanently delete one or more test runs by their IDs. This action cannot be undone and will remove all nested elements including phases, measurements, and run attachments from both database and S3 storage.

        :param ids: Array of run IDs to delete.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.RunDeleteRequest(
            ids=ids,
        )

        req = self._build_request(
            method="DELETE",
            path="/v2/runs",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="run-delete",
                oauth2_scopes=[],
                security_source=get_security_from_env(
                    self.sdk_configuration.security, models.Security
                ),
            ),
            request=req,
            error_status_codes=["404", "4XX", "500", "5XX"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return utils.unmarshal_json(http_res.text, models.RunDeleteResponse)
        if utils.match_response(http_res, "404", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ErrorNOTFOUNDData
            )
            raise errors.ErrorNOTFOUND(data=response_data)
        if utils.match_response(http_res, "500", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ERRORINTERNALSERVERERRORData
            )
            raise errors.ERRORINTERNALSERVERERROR(data=response_data)
        if utils.match_response(http_res, "4XX", "*"):
            http_res_text = utils.stream_to_text(http_res)
            raise errors.APIError(
                "API error occurred", http_res.status_code, http_res_text, http_res
            )
        if utils.match_response(http_res, "5XX", "*"):
            http_res_text = utils.stream_to_text(http_res)
            raise errors.APIError(
                "API error occurred", http_res.status_code, http_res_text, http_res
            )

        content_type = http_res.headers.get("Content-Type")
        http_res_text = utils.stream_to_text(http_res)
        raise errors.APIError(
            f"Unexpected response received (code: {http_res.status_code}, type: {content_type})",
            http_res.status_code,
            http_res_text,
            http_res,
        )

    async def delete_async(
        self,
        *,
        ids: List[str],
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.RunDeleteResponse:
        r"""Delete runs

        Permanently delete one or more test runs by their IDs. This action cannot be undone and will remove all nested elements including phases, measurements, and run attachments from both database and S3 storage.

        :param ids: Array of run IDs to delete.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.RunDeleteRequest(
            ids=ids,
        )

        req = self._build_request_async(
            method="DELETE",
            path="/v2/runs",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=False,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="run-delete",
                oauth2_scopes=[],
                security_source=get_security_from_env(
                    self.sdk_configuration.security, models.Security
                ),
            ),
            request=req,
            error_status_codes=["404", "4XX", "500", "5XX"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return utils.unmarshal_json(http_res.text, models.RunDeleteResponse)
        if utils.match_response(http_res, "404", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ErrorNOTFOUNDData
            )
            raise errors.ErrorNOTFOUND(data=response_data)
        if utils.match_response(http_res, "500", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ERRORINTERNALSERVERERRORData
            )
            raise errors.ERRORINTERNALSERVERERROR(data=response_data)
        if utils.match_response(http_res, "4XX", "*"):
            http_res_text = await utils.stream_to_text_async(http_res)
            raise errors.APIError(
                "API error occurred", http_res.status_code, http_res_text, http_res
            )
        if utils.match_response(http_res, "5XX", "*"):
            http_res_text = await utils.stream_to_text_async(http_res)
            raise errors.APIError(
                "API error occurred", http_res.status_code, http_res_text, http_res
            )

        content_type = http_res.headers.get("Content-Type")
        http_res_text = await utils.stream_to_text_async(http_res)
        raise errors.APIError(
            f"Unexpected response received (code: {http_res.status_code}, type: {content_type})",
            http_res.status_code,
            http_res_text,
            http_res,
        )

    def get(
        self,
        *,
        id: str,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.RunGetResponse:
        r"""Get run

        Retrieve a single test run by its ID. Returns comprehensive run data including metadata, phases, measurements, and logs.

        :param id: ID of the run to retrieve.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.RunGetRequest(
            id=id,
        )

        req = self._build_request(
            method="GET",
            path="/v2/runs/{id}",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="run-get",
                oauth2_scopes=[],
                security_source=get_security_from_env(
                    self.sdk_configuration.security, models.Security
                ),
            ),
            request=req,
            error_status_codes=["404", "4XX", "500", "5XX"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return utils.unmarshal_json(http_res.text, models.RunGetResponse)
        if utils.match_response(http_res, "404", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ErrorNOTFOUNDData
            )
            raise errors.ErrorNOTFOUND(data=response_data)
        if utils.match_response(http_res, "500", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ERRORINTERNALSERVERERRORData
            )
            raise errors.ERRORINTERNALSERVERERROR(data=response_data)
        if utils.match_response(http_res, "4XX", "*"):
            http_res_text = utils.stream_to_text(http_res)
            raise errors.APIError(
                "API error occurred", http_res.status_code, http_res_text, http_res
            )
        if utils.match_response(http_res, "5XX", "*"):
            http_res_text = utils.stream_to_text(http_res)
            raise errors.APIError(
                "API error occurred", http_res.status_code, http_res_text, http_res
            )

        content_type = http_res.headers.get("Content-Type")
        http_res_text = utils.stream_to_text(http_res)
        raise errors.APIError(
            f"Unexpected response received (code: {http_res.status_code}, type: {content_type})",
            http_res.status_code,
            http_res_text,
            http_res,
        )

    async def get_async(
        self,
        *,
        id: str,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.RunGetResponse:
        r"""Get run

        Retrieve a single test run by its ID. Returns comprehensive run data including metadata, phases, measurements, and logs.

        :param id: ID of the run to retrieve.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.RunGetRequest(
            id=id,
        )

        req = self._build_request_async(
            method="GET",
            path="/v2/runs/{id}",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=False,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            security=self.sdk_configuration.security,
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="run-get",
                oauth2_scopes=[],
                security_source=get_security_from_env(
                    self.sdk_configuration.security, models.Security
                ),
            ),
            request=req,
            error_status_codes=["404", "4XX", "500", "5XX"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return utils.unmarshal_json(http_res.text, models.RunGetResponse)
        if utils.match_response(http_res, "404", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ErrorNOTFOUNDData
            )
            raise errors.ErrorNOTFOUND(data=response_data)
        if utils.match_response(http_res, "500", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ERRORINTERNALSERVERERRORData
            )
            raise errors.ERRORINTERNALSERVERERROR(data=response_data)
        if utils.match_response(http_res, "4XX", "*"):
            http_res_text = await utils.stream_to_text_async(http_res)
            raise errors.APIError(
                "API error occurred", http_res.status_code, http_res_text, http_res
            )
        if utils.match_response(http_res, "5XX", "*"):
            http_res_text = await utils.stream_to_text_async(http_res)
            raise errors.APIError(
                "API error occurred", http_res.status_code, http_res_text, http_res
            )

        content_type = http_res.headers.get("Content-Type")
        http_res_text = await utils.stream_to_text_async(http_res)
        raise errors.APIError(
            f"Unexpected response received (code: {http_res.status_code}, type: {content_type})",
            http_res.status_code,
            http_res_text,
            http_res,
        )

    def update(
        self,
        *,
        id: str,
        attachments: Optional[List[str]] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.RunUpdateResponse:
        r"""Update run

        Update a test run with new attachments. The upload must be initialized and completed before calling this endpoint.

        :param id: Unique identifier of the run to update.
        :param attachments: Array of upload IDs to attach to the run.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.RunUpdateRequest(
            id=id,
            request_body=models.RunUpdateRequestBody(
                attachments=attachments,
            ),
        )

        req = self._build_request(
            method="PATCH",
            path="/v2/runs/{id}",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request.request_body, False, False, "json", models.RunUpdateRequestBody
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = self.do_request(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="run-update",
                oauth2_scopes=[],
                security_source=get_security_from_env(
                    self.sdk_configuration.security, models.Security
                ),
            ),
            request=req,
            error_status_codes=["404", "4XX", "500", "5XX"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return utils.unmarshal_json(http_res.text, models.RunUpdateResponse)
        if utils.match_response(http_res, "404", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ErrorNOTFOUNDData
            )
            raise errors.ErrorNOTFOUND(data=response_data)
        if utils.match_response(http_res, "500", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ERRORINTERNALSERVERERRORData
            )
            raise errors.ERRORINTERNALSERVERERROR(data=response_data)
        if utils.match_response(http_res, "4XX", "*"):
            http_res_text = utils.stream_to_text(http_res)
            raise errors.APIError(
                "API error occurred", http_res.status_code, http_res_text, http_res
            )
        if utils.match_response(http_res, "5XX", "*"):
            http_res_text = utils.stream_to_text(http_res)
            raise errors.APIError(
                "API error occurred", http_res.status_code, http_res_text, http_res
            )

        content_type = http_res.headers.get("Content-Type")
        http_res_text = utils.stream_to_text(http_res)
        raise errors.APIError(
            f"Unexpected response received (code: {http_res.status_code}, type: {content_type})",
            http_res.status_code,
            http_res_text,
            http_res,
        )

    async def update_async(
        self,
        *,
        id: str,
        attachments: Optional[List[str]] = None,
        retries: OptionalNullable[utils.RetryConfig] = UNSET,
        server_url: Optional[str] = None,
        timeout_ms: Optional[int] = None,
        http_headers: Optional[Mapping[str, str]] = None,
    ) -> models.RunUpdateResponse:
        r"""Update run

        Update a test run with new attachments. The upload must be initialized and completed before calling this endpoint.

        :param id: Unique identifier of the run to update.
        :param attachments: Array of upload IDs to attach to the run.
        :param retries: Override the default retry configuration for this method
        :param server_url: Override the default server URL for this method
        :param timeout_ms: Override the default request timeout configuration for this method in milliseconds
        :param http_headers: Additional headers to set or replace on requests.
        """
        base_url = None
        url_variables = None
        if timeout_ms is None:
            timeout_ms = self.sdk_configuration.timeout_ms

        if server_url is not None:
            base_url = server_url
        else:
            base_url = self._get_url(base_url, url_variables)

        request = models.RunUpdateRequest(
            id=id,
            request_body=models.RunUpdateRequestBody(
                attachments=attachments,
            ),
        )

        req = self._build_request_async(
            method="PATCH",
            path="/v2/runs/{id}",
            base_url=base_url,
            url_variables=url_variables,
            request=request,
            request_body_required=True,
            request_has_path_params=True,
            request_has_query_params=True,
            user_agent_header="user-agent",
            accept_header_value="application/json",
            http_headers=http_headers,
            security=self.sdk_configuration.security,
            get_serialized_body=lambda: utils.serialize_request_body(
                request.request_body, False, False, "json", models.RunUpdateRequestBody
            ),
            timeout_ms=timeout_ms,
        )

        if retries == UNSET:
            if self.sdk_configuration.retry_config is not UNSET:
                retries = self.sdk_configuration.retry_config

        retry_config = None
        if isinstance(retries, utils.RetryConfig):
            retry_config = (retries, ["429", "500", "502", "503", "504"])

        http_res = await self.do_request_async(
            hook_ctx=HookContext(
                config=self.sdk_configuration,
                base_url=base_url or "",
                operation_id="run-update",
                oauth2_scopes=[],
                security_source=get_security_from_env(
                    self.sdk_configuration.security, models.Security
                ),
            ),
            request=req,
            error_status_codes=["404", "4XX", "500", "5XX"],
            retry_config=retry_config,
        )

        response_data: Any = None
        if utils.match_response(http_res, "200", "application/json"):
            return utils.unmarshal_json(http_res.text, models.RunUpdateResponse)
        if utils.match_response(http_res, "404", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ErrorNOTFOUNDData
            )
            raise errors.ErrorNOTFOUND(data=response_data)
        if utils.match_response(http_res, "500", "application/json"):
            response_data = utils.unmarshal_json(
                http_res.text, errors.ERRORINTERNALSERVERERRORData
            )
            raise errors.ERRORINTERNALSERVERERROR(data=response_data)
        if utils.match_response(http_res, "4XX", "*"):
            http_res_text = await utils.stream_to_text_async(http_res)
            raise errors.APIError(
                "API error occurred", http_res.status_code, http_res_text, http_res
            )
        if utils.match_response(http_res, "5XX", "*"):
            http_res_text = await utils.stream_to_text_async(http_res)
            raise errors.APIError(
                "API error occurred", http_res.status_code, http_res_text, http_res
            )

        content_type = http_res.headers.get("Content-Type")
        http_res_text = await utils.stream_to_text_async(http_res)
        raise errors.APIError(
            f"Unexpected response received (code: {http_res.status_code}, type: {content_type})",
            http_res.status_code,
            http_res_text,
            http_res,
        )
