# coding: utf-8

"""
Trading API

API for automated trading and exchange interface. This API is used to trade on the exchange and manage bots, API keys, orders, and more.

The version of the OpenAPI document: 2.0.0
Generated by OpenAPI Generator (https://openapi-generator.tech)

Do not edit the class manually.
"""  # noqa: E501


from __future__ import annotations

import json
import pprint
import re  # noqa: F401
from typing import Any, ClassVar, Dict, List, Optional, Set

from pydantic import (
    BaseModel,
    ConfigDict,
    Field,
    StrictBool,
    StrictInt,
    StrictStr,
    field_validator,
)
from typing_extensions import Self

from crypticorn.trade.client.models.trading_action_type import TradingActionType


class Order(BaseModel):
    """
    Response model for orders. This is the model that is returned by the API and the database.
    """  # noqa: E501

    created_at: StrictInt = Field(description="Timestamp of creation")
    updated_at: StrictInt = Field(description="Timestamp of last update")
    id: StrictStr = Field(description="Unique identifier for the resource")
    exchange: StrictStr = Field(description="Exchange name. Of type Exchange")
    symbol: StrictStr = Field(description="Trading symbol on exchange")
    action_type: TradingActionType = Field(
        description="Type of trading action. Of type TradingActionType"
    )
    status: StrictStr = Field(
        description="Trade status of the order. Of type OrderStatus"
    )
    market_type: StrictStr = Field(
        description="Market type of the order. Of type MarketType"
    )
    trading_action_id: Optional[StrictStr] = None
    execution_id: Optional[StrictStr] = None
    exchange_order_id: Optional[StrictStr] = None
    position_id: Optional[StrictStr] = None
    api_key_id: Optional[StrictStr] = None
    bot_id: Optional[StrictStr] = None
    client_order_id: Optional[StrictStr] = None
    common_symbol: Optional[StrictStr] = None
    price: StrictStr = Field(description="Price of the order")
    margin_mode: Optional[StrictStr] = None
    status_code: StrictStr = Field(description="API error identifier")
    filled_perc: Optional[StrictStr] = Field(
        default="0", description="Percentage of the order filled"
    )
    filled_qty: Optional[StrictStr] = Field(
        default="0",
        description="Quantity filled. Needed for pnl calculation. In the symbol's base currency.",
    )
    sent_qty: Optional[StrictStr] = Field(
        default="0",
        description="Quantity sent to the exchange. In the symbol's base currency.",
    )
    fee: Optional[StrictStr] = None
    leverage: Optional[StrictInt] = None
    order_details: Optional[Dict[str, Any]] = Field(
        default=None, description="Exchange specific details of the order"
    )
    pnl: Optional[StrictStr] = None
    order_time: Optional[StrictInt] = None
    is_lost: Optional[StrictBool] = None
    __properties: ClassVar[List[str]] = [
        "created_at",
        "updated_at",
        "id",
        "exchange",
        "symbol",
        "action_type",
        "status",
        "market_type",
        "trading_action_id",
        "execution_id",
        "exchange_order_id",
        "position_id",
        "api_key_id",
        "bot_id",
        "client_order_id",
        "common_symbol",
        "price",
        "margin_mode",
        "status_code",
        "filled_perc",
        "filled_qty",
        "sent_qty",
        "fee",
        "leverage",
        "order_details",
        "pnl",
        "order_time",
        "is_lost",
    ]

    @field_validator("exchange")
    def exchange_validate_enum(cls, value):
        """Validates the enum"""
        if value not in set(["hyperliquid"]):
            raise ValueError("must be one of enum values ('hyperliquid')")
        return value

    @field_validator("status")
    def status_validate_enum(cls, value):
        """Validates the enum"""
        if value not in set(
            ["new", "filled", "partially_filled", "cancelled", "failed"]
        ):
            raise ValueError(
                "must be one of enum values ('new', 'filled', 'partially_filled', 'cancelled', 'failed')"
            )
        return value

    @field_validator("market_type")
    def market_type_validate_enum(cls, value):
        """Validates the enum"""
        if value not in set(["spot", "futures"]):
            raise ValueError("must be one of enum values ('spot', 'futures')")
        return value

    @field_validator("margin_mode")
    def margin_mode_validate_enum(cls, value):
        """Validates the enum"""
        if value is None:
            return value

        if value not in set(["isolated", "cross"]):
            raise ValueError("must be one of enum values ('isolated', 'cross')")
        return value

    @field_validator("status_code")
    def status_code_validate_enum(cls, value):
        """Validates the enum"""
        if value not in set(
            [
                "unknown_error",
                "invalid_data_request",
                "object_not_found",
                "forbidden",
                "exchange_key_already_exists",
                "exchange_key_in_use",
                "order_size_too_small",
                "order_size_too_large",
                "bot_stopping_completed",
                "success",
                "strategy_disabled",
                "bot_already_deleted",
                "object_already_exists",
                "invalid_exchange_key",
                "hedge_mode_not_active",
                "strategy_not_supporting_exchange",
                "margin_mode_clash",
                "order_price_invalid",
                "insufficient_margin",
                "insufficient_balance",
                "exchange_invalid_parameter",
                "exchange_invalid_signature",
                "exchange_rate_limit",
                "order_not_found",
                "symbol_not_found",
                "order_limit_exceeded",
                "exchange_system_error",
                "exchange_system_busy",
                "rpc_timeout",
                "position_not_found",
                "leverage_exceeded",
                "trading_suspended",
                "exchange_maintenance",
                "exchange_ip_restricted",
                "post_only_rejected",
                "content_type_error",
                "exchange_http_error",
                "orphan_close_order",
                "cancelled_open_order",
                "failed_open_order",
                "orphan_open_order",
                "trading_action_skipped_bot_stopping",
                "trading_action_expired",
                "black_swan",
            ]
        ):
            raise ValueError(
                "must be one of enum values ('unknown_error', 'invalid_data_request', 'object_not_found', 'forbidden', 'exchange_key_already_exists', 'exchange_key_in_use', 'order_size_too_small', 'order_size_too_large', 'bot_stopping_completed', 'success', 'strategy_disabled', 'bot_already_deleted', 'object_already_exists', 'invalid_exchange_key', 'hedge_mode_not_active', 'strategy_not_supporting_exchange', 'margin_mode_clash', 'order_price_invalid', 'insufficient_margin', 'insufficient_balance', 'exchange_invalid_parameter', 'exchange_invalid_signature', 'exchange_rate_limit', 'order_not_found', 'symbol_not_found', 'order_limit_exceeded', 'exchange_system_error', 'exchange_system_busy', 'rpc_timeout', 'position_not_found', 'leverage_exceeded', 'trading_suspended', 'exchange_maintenance', 'exchange_ip_restricted', 'post_only_rejected', 'content_type_error', 'exchange_http_error', 'orphan_close_order', 'cancelled_open_order', 'failed_open_order', 'orphan_open_order', 'trading_action_skipped_bot_stopping', 'trading_action_expired', 'black_swan')"
            )
        return value

    model_config = ConfigDict(
        populate_by_name=True,
        validate_assignment=True,
        protected_namespaces=(),
    )

    def to_str(self) -> str:
        """Returns the string representation of the model using alias"""
        return pprint.pformat(self.model_dump(by_alias=True))

    def to_json(self) -> str:
        """Returns the JSON representation of the model using alias"""
        # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
        return json.dumps(self.to_dict())

    @classmethod
    def from_json(cls, json_str: str) -> Optional[Self]:
        """Create an instance of Order from a JSON string"""
        return cls.from_dict(json.loads(json_str))

    def to_dict(self) -> Dict[str, Any]:
        """Return the dictionary representation of the model using alias.

        This has the following differences from calling pydantic's
        `self.model_dump(by_alias=True)`:

        * `None` is only added to the output dict for nullable fields that
          were set at model initialization. Other fields with value `None`
          are ignored.
        """
        excluded_fields: Set[str] = set([])

        _dict = self.model_dump(
            by_alias=True,
            exclude=excluded_fields,
            exclude_none=True,
        )
        # set to None if trading_action_id (nullable) is None
        # and model_fields_set contains the field
        if (
            self.trading_action_id is None
            and "trading_action_id" in self.model_fields_set
        ):
            _dict["trading_action_id"] = None

        # set to None if execution_id (nullable) is None
        # and model_fields_set contains the field
        if self.execution_id is None and "execution_id" in self.model_fields_set:
            _dict["execution_id"] = None

        # set to None if exchange_order_id (nullable) is None
        # and model_fields_set contains the field
        if (
            self.exchange_order_id is None
            and "exchange_order_id" in self.model_fields_set
        ):
            _dict["exchange_order_id"] = None

        # set to None if position_id (nullable) is None
        # and model_fields_set contains the field
        if self.position_id is None and "position_id" in self.model_fields_set:
            _dict["position_id"] = None

        # set to None if api_key_id (nullable) is None
        # and model_fields_set contains the field
        if self.api_key_id is None and "api_key_id" in self.model_fields_set:
            _dict["api_key_id"] = None

        # set to None if bot_id (nullable) is None
        # and model_fields_set contains the field
        if self.bot_id is None and "bot_id" in self.model_fields_set:
            _dict["bot_id"] = None

        # set to None if client_order_id (nullable) is None
        # and model_fields_set contains the field
        if self.client_order_id is None and "client_order_id" in self.model_fields_set:
            _dict["client_order_id"] = None

        # set to None if common_symbol (nullable) is None
        # and model_fields_set contains the field
        if self.common_symbol is None and "common_symbol" in self.model_fields_set:
            _dict["common_symbol"] = None

        # set to None if margin_mode (nullable) is None
        # and model_fields_set contains the field
        if self.margin_mode is None and "margin_mode" in self.model_fields_set:
            _dict["margin_mode"] = None

        # set to None if fee (nullable) is None
        # and model_fields_set contains the field
        if self.fee is None and "fee" in self.model_fields_set:
            _dict["fee"] = None

        # set to None if leverage (nullable) is None
        # and model_fields_set contains the field
        if self.leverage is None and "leverage" in self.model_fields_set:
            _dict["leverage"] = None

        # set to None if pnl (nullable) is None
        # and model_fields_set contains the field
        if self.pnl is None and "pnl" in self.model_fields_set:
            _dict["pnl"] = None

        # set to None if order_time (nullable) is None
        # and model_fields_set contains the field
        if self.order_time is None and "order_time" in self.model_fields_set:
            _dict["order_time"] = None

        # set to None if is_lost (nullable) is None
        # and model_fields_set contains the field
        if self.is_lost is None and "is_lost" in self.model_fields_set:
            _dict["is_lost"] = None

        return _dict

    @classmethod
    def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
        """Create an instance of Order from a dict"""
        if obj is None:
            return None

        if not isinstance(obj, dict):
            return cls.model_validate(obj)

        _obj = cls.model_validate(
            {
                "created_at": obj.get("created_at"),
                "updated_at": obj.get("updated_at"),
                "id": obj.get("id"),
                "exchange": obj.get("exchange"),
                "symbol": obj.get("symbol"),
                "action_type": obj.get("action_type"),
                "status": obj.get("status"),
                "market_type": obj.get("market_type"),
                "trading_action_id": obj.get("trading_action_id"),
                "execution_id": obj.get("execution_id"),
                "exchange_order_id": obj.get("exchange_order_id"),
                "position_id": obj.get("position_id"),
                "api_key_id": obj.get("api_key_id"),
                "bot_id": obj.get("bot_id"),
                "client_order_id": obj.get("client_order_id"),
                "common_symbol": obj.get("common_symbol"),
                "price": obj.get("price"),
                "margin_mode": obj.get("margin_mode"),
                "status_code": obj.get("status_code"),
                "filled_perc": (
                    obj.get("filled_perc")
                    if obj.get("filled_perc") is not None
                    else "0"
                ),
                "filled_qty": (
                    obj.get("filled_qty") if obj.get("filled_qty") is not None else "0"
                ),
                "sent_qty": (
                    obj.get("sent_qty") if obj.get("sent_qty") is not None else "0"
                ),
                "fee": obj.get("fee"),
                "leverage": obj.get("leverage"),
                "order_details": obj.get("order_details"),
                "pnl": obj.get("pnl"),
                "order_time": obj.get("order_time"),
                "is_lost": obj.get("is_lost"),
            }
        )
        return _obj
