# This file was auto-generated by Fern from our API Definition.

import typing
from ..core.client_wrapper import SyncClientWrapper
from ..types.model_names import ModelNames
from ..types.named_quantity import NamedQuantity
from ..types.parameter_bound import ParameterBound
from ..types.named_quantity_list import NamedQuantityList
from ..core.request_options import RequestOptions
from ..types.optimizer_result import OptimizerResult
from ..core.serialization import convert_and_respect_annotation_metadata
from ..core.pydantic_utilities import parse_obj_as
from ..errors.unprocessable_entity_error import UnprocessableEntityError
from ..types.http_validation_error import HttpValidationError
from json.decoder import JSONDecodeError
from ..core.api_error import ApiError
from ..types.targets import Targets
from ..types.input_values import InputValues
from ..types.evaluate_result import EvaluateResult
from ..types.list_models_result import ListModelsResult
from ..core.client_wrapper import AsyncClientWrapper

# this is used as the default value for optional parameters
OMIT = typing.cast(typing.Any, ...)


class DigitalTwinClient:
    def __init__(self, *, client_wrapper: SyncClientWrapper):
        self._client_wrapper = client_wrapper

    def optimize(
        self,
        *,
        model_name: ModelNames,
        parameters: typing.Sequence[NamedQuantity],
        parameter_bounds: typing.Sequence[ParameterBound],
        constants: typing.Sequence[NamedQuantity],
        input: NamedQuantityList,
        target: NamedQuantityList,
        tolerance: typing.Optional[float] = OMIT,
        max_time: typing.Optional[int] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> OptimizerResult:
        """
        Fits a digital twin model to input data using numerical optimization.

            Accepts parameter guesses, bounds, inputs, targets, and constants — all with units —
            and returns the optimized parameters that minimize the model's error using a selected cost function.

            Args:
                OptimizerBody: The input data and optimization parameters.
            Returns:
                OptimizerResult: The result of the optimization.

        Parameters
        ----------
        model_name : ModelNames

        parameters : typing.Sequence[NamedQuantity]

        parameter_bounds : typing.Sequence[ParameterBound]

        constants : typing.Sequence[NamedQuantity]

        input : NamedQuantityList

        target : NamedQuantityList

        tolerance : typing.Optional[float]

        max_time : typing.Optional[int]

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        OptimizerResult
            Successful Response

        Examples
        --------
        from axiomatic import (
            Axiomatic,
            NamedQuantity,
            NamedQuantityList,
            ParameterBound,
            Quantity,
        )

        client = Axiomatic(
            api_key="YOUR_API_KEY",
        )
        client.digital_twin.optimize(
            model_name="PNJunctionSiliconOnInsulatorWavelength",
            parameters=[
                NamedQuantity(
                    name="name",
                    value=Quantity(
                        magnitude=1.1,
                        unit="unit",
                    ),
                )
            ],
            parameter_bounds=[
                ParameterBound(
                    name="name",
                    lower=Quantity(
                        magnitude=1.1,
                        unit="unit",
                    ),
                    upper=Quantity(
                        magnitude=1.1,
                        unit="unit",
                    ),
                )
            ],
            constants=[
                NamedQuantity(
                    name="name",
                    value=Quantity(
                        magnitude=1.1,
                        unit="unit",
                    ),
                )
            ],
            input=NamedQuantityList(
                name="name",
                unit="unit",
                magnitudes=[1.1],
            ),
            target=NamedQuantityList(
                name="name",
                unit="unit",
                magnitudes=[1.1],
            ),
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "digital-twin/optimize",
            method="POST",
            json={
                "model_name": model_name,
                "parameters": convert_and_respect_annotation_metadata(
                    object_=parameters, annotation=typing.Sequence[NamedQuantity], direction="write"
                ),
                "parameter_bounds": convert_and_respect_annotation_metadata(
                    object_=parameter_bounds, annotation=typing.Sequence[ParameterBound], direction="write"
                ),
                "constants": convert_and_respect_annotation_metadata(
                    object_=constants, annotation=typing.Sequence[NamedQuantity], direction="write"
                ),
                "input": convert_and_respect_annotation_metadata(
                    object_=input, annotation=NamedQuantityList, direction="write"
                ),
                "target": convert_and_respect_annotation_metadata(
                    object_=target, annotation=NamedQuantityList, direction="write"
                ),
                "tolerance": tolerance,
                "max_time": max_time,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    OptimizerResult,
                    parse_obj_as(
                        type_=OptimizerResult,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    typing.cast(
                        HttpValidationError,
                        parse_obj_as(
                            type_=HttpValidationError,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    def evaluate(
        self,
        *,
        model_name: ModelNames,
        parameters: typing.Sequence[NamedQuantity],
        target_function: Targets,
        target_unit: str,
        input_values: InputValues,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> EvaluateResult:
        """
        Evaluate Digital Twin Model

            This endpoint takes model parameters and target functions as input,
            then returns the evaluated values for each target function using the
            specified model. It can be used for model validation, simulation,
            and prediction tasks.

        Parameters
        ----------
        model_name : ModelNames

        parameters : typing.Sequence[NamedQuantity]

        target_function : Targets

        target_unit : str

        input_values : InputValues

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        EvaluateResult
            Successful Response

        Examples
        --------
        from axiomatic import (
            Axiomatic,
            EvaluatePoint,
            InputValues,
            NamedQuantity,
            Quantity,
        )

        client = Axiomatic(
            api_key="YOUR_API_KEY",
        )
        client.digital_twin.evaluate(
            model_name="PNJunctionSiliconOnInsulatorWavelength",
            parameters=[
                NamedQuantity(
                    name="name",
                    value=Quantity(
                        magnitude=1.1,
                        unit="unit",
                    ),
                )
            ],
            target_function="delta_n_eff",
            target_unit="target_unit",
            input_values=InputValues(
                name="name",
                unit="unit",
                points=[
                    EvaluatePoint(
                        value_x=1.1,
                        percentage_coord_x=1.1,
                        value_y=1.1,
                        percentage_coord_y=1.1,
                    )
                ],
            ),
        )
        """
        _response = self._client_wrapper.httpx_client.request(
            "digital-twin/evaluate",
            method="POST",
            json={
                "model_name": model_name,
                "parameters": convert_and_respect_annotation_metadata(
                    object_=parameters, annotation=typing.Sequence[NamedQuantity], direction="write"
                ),
                "target_function": target_function,
                "target_unit": target_unit,
                "input_values": convert_and_respect_annotation_metadata(
                    object_=input_values, annotation=InputValues, direction="write"
                ),
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    EvaluateResult,
                    parse_obj_as(
                        type_=EvaluateResult,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    typing.cast(
                        HttpValidationError,
                        parse_obj_as(
                            type_=HttpValidationError,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    def list_models(self, *, request_options: typing.Optional[RequestOptions] = None) -> ListModelsResult:
        """
        Retrieves a list of available digital twin models and their parameter definitions.

            Returns the name, description (if available), and parameter details
            (name, unit, lower and upper bound) for each model.

        Parameters
        ----------
        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        ListModelsResult
            Successful Response

        Examples
        --------
        from axiomatic import Axiomatic

        client = Axiomatic(
            api_key="YOUR_API_KEY",
        )
        client.digital_twin.list_models()
        """
        _response = self._client_wrapper.httpx_client.request(
            "digital-twin/models",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    ListModelsResult,
                    parse_obj_as(
                        type_=ListModelsResult,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)


class AsyncDigitalTwinClient:
    def __init__(self, *, client_wrapper: AsyncClientWrapper):
        self._client_wrapper = client_wrapper

    async def optimize(
        self,
        *,
        model_name: ModelNames,
        parameters: typing.Sequence[NamedQuantity],
        parameter_bounds: typing.Sequence[ParameterBound],
        constants: typing.Sequence[NamedQuantity],
        input: NamedQuantityList,
        target: NamedQuantityList,
        tolerance: typing.Optional[float] = OMIT,
        max_time: typing.Optional[int] = OMIT,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> OptimizerResult:
        """
        Fits a digital twin model to input data using numerical optimization.

            Accepts parameter guesses, bounds, inputs, targets, and constants — all with units —
            and returns the optimized parameters that minimize the model's error using a selected cost function.

            Args:
                OptimizerBody: The input data and optimization parameters.
            Returns:
                OptimizerResult: The result of the optimization.

        Parameters
        ----------
        model_name : ModelNames

        parameters : typing.Sequence[NamedQuantity]

        parameter_bounds : typing.Sequence[ParameterBound]

        constants : typing.Sequence[NamedQuantity]

        input : NamedQuantityList

        target : NamedQuantityList

        tolerance : typing.Optional[float]

        max_time : typing.Optional[int]

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        OptimizerResult
            Successful Response

        Examples
        --------
        import asyncio

        from axiomatic import (
            AsyncAxiomatic,
            NamedQuantity,
            NamedQuantityList,
            ParameterBound,
            Quantity,
        )

        client = AsyncAxiomatic(
            api_key="YOUR_API_KEY",
        )


        async def main() -> None:
            await client.digital_twin.optimize(
                model_name="PNJunctionSiliconOnInsulatorWavelength",
                parameters=[
                    NamedQuantity(
                        name="name",
                        value=Quantity(
                            magnitude=1.1,
                            unit="unit",
                        ),
                    )
                ],
                parameter_bounds=[
                    ParameterBound(
                        name="name",
                        lower=Quantity(
                            magnitude=1.1,
                            unit="unit",
                        ),
                        upper=Quantity(
                            magnitude=1.1,
                            unit="unit",
                        ),
                    )
                ],
                constants=[
                    NamedQuantity(
                        name="name",
                        value=Quantity(
                            magnitude=1.1,
                            unit="unit",
                        ),
                    )
                ],
                input=NamedQuantityList(
                    name="name",
                    unit="unit",
                    magnitudes=[1.1],
                ),
                target=NamedQuantityList(
                    name="name",
                    unit="unit",
                    magnitudes=[1.1],
                ),
            )


        asyncio.run(main())
        """
        _response = await self._client_wrapper.httpx_client.request(
            "digital-twin/optimize",
            method="POST",
            json={
                "model_name": model_name,
                "parameters": convert_and_respect_annotation_metadata(
                    object_=parameters, annotation=typing.Sequence[NamedQuantity], direction="write"
                ),
                "parameter_bounds": convert_and_respect_annotation_metadata(
                    object_=parameter_bounds, annotation=typing.Sequence[ParameterBound], direction="write"
                ),
                "constants": convert_and_respect_annotation_metadata(
                    object_=constants, annotation=typing.Sequence[NamedQuantity], direction="write"
                ),
                "input": convert_and_respect_annotation_metadata(
                    object_=input, annotation=NamedQuantityList, direction="write"
                ),
                "target": convert_and_respect_annotation_metadata(
                    object_=target, annotation=NamedQuantityList, direction="write"
                ),
                "tolerance": tolerance,
                "max_time": max_time,
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    OptimizerResult,
                    parse_obj_as(
                        type_=OptimizerResult,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    typing.cast(
                        HttpValidationError,
                        parse_obj_as(
                            type_=HttpValidationError,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    async def evaluate(
        self,
        *,
        model_name: ModelNames,
        parameters: typing.Sequence[NamedQuantity],
        target_function: Targets,
        target_unit: str,
        input_values: InputValues,
        request_options: typing.Optional[RequestOptions] = None,
    ) -> EvaluateResult:
        """
        Evaluate Digital Twin Model

            This endpoint takes model parameters and target functions as input,
            then returns the evaluated values for each target function using the
            specified model. It can be used for model validation, simulation,
            and prediction tasks.

        Parameters
        ----------
        model_name : ModelNames

        parameters : typing.Sequence[NamedQuantity]

        target_function : Targets

        target_unit : str

        input_values : InputValues

        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        EvaluateResult
            Successful Response

        Examples
        --------
        import asyncio

        from axiomatic import (
            AsyncAxiomatic,
            EvaluatePoint,
            InputValues,
            NamedQuantity,
            Quantity,
        )

        client = AsyncAxiomatic(
            api_key="YOUR_API_KEY",
        )


        async def main() -> None:
            await client.digital_twin.evaluate(
                model_name="PNJunctionSiliconOnInsulatorWavelength",
                parameters=[
                    NamedQuantity(
                        name="name",
                        value=Quantity(
                            magnitude=1.1,
                            unit="unit",
                        ),
                    )
                ],
                target_function="delta_n_eff",
                target_unit="target_unit",
                input_values=InputValues(
                    name="name",
                    unit="unit",
                    points=[
                        EvaluatePoint(
                            value_x=1.1,
                            percentage_coord_x=1.1,
                            value_y=1.1,
                            percentage_coord_y=1.1,
                        )
                    ],
                ),
            )


        asyncio.run(main())
        """
        _response = await self._client_wrapper.httpx_client.request(
            "digital-twin/evaluate",
            method="POST",
            json={
                "model_name": model_name,
                "parameters": convert_and_respect_annotation_metadata(
                    object_=parameters, annotation=typing.Sequence[NamedQuantity], direction="write"
                ),
                "target_function": target_function,
                "target_unit": target_unit,
                "input_values": convert_and_respect_annotation_metadata(
                    object_=input_values, annotation=InputValues, direction="write"
                ),
            },
            headers={
                "content-type": "application/json",
            },
            request_options=request_options,
            omit=OMIT,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    EvaluateResult,
                    parse_obj_as(
                        type_=EvaluateResult,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            if _response.status_code == 422:
                raise UnprocessableEntityError(
                    typing.cast(
                        HttpValidationError,
                        parse_obj_as(
                            type_=HttpValidationError,  # type: ignore
                            object_=_response.json(),
                        ),
                    )
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)

    async def list_models(self, *, request_options: typing.Optional[RequestOptions] = None) -> ListModelsResult:
        """
        Retrieves a list of available digital twin models and their parameter definitions.

            Returns the name, description (if available), and parameter details
            (name, unit, lower and upper bound) for each model.

        Parameters
        ----------
        request_options : typing.Optional[RequestOptions]
            Request-specific configuration.

        Returns
        -------
        ListModelsResult
            Successful Response

        Examples
        --------
        import asyncio

        from axiomatic import AsyncAxiomatic

        client = AsyncAxiomatic(
            api_key="YOUR_API_KEY",
        )


        async def main() -> None:
            await client.digital_twin.list_models()


        asyncio.run(main())
        """
        _response = await self._client_wrapper.httpx_client.request(
            "digital-twin/models",
            method="GET",
            request_options=request_options,
        )
        try:
            if 200 <= _response.status_code < 300:
                return typing.cast(
                    ListModelsResult,
                    parse_obj_as(
                        type_=ListModelsResult,  # type: ignore
                        object_=_response.json(),
                    ),
                )
            _response_json = _response.json()
        except JSONDecodeError:
            raise ApiError(status_code=_response.status_code, body=_response.text)
        raise ApiError(status_code=_response.status_code, body=_response_json)
