# coding: utf-8

"""
    LUSID API

    FINBOURNE Technology  # noqa: E501

    Contact: info@finbourne.com
    Generated by OpenAPI Generator (https://openapi-generator.tech)

    Do not edit the class manually.
"""


from __future__ import annotations
import pprint
import re  # noqa: F401
import json


from typing import List, Dict, Optional, Any, Union, TYPE_CHECKING
from typing_extensions import Annotated
from pydantic.v1 import BaseModel, StrictStr, StrictInt, StrictBool, StrictFloat, StrictBytes, Field, validator, ValidationError, conlist, constr
from datetime import datetime
from lusid.models.collateral import Collateral
from lusid.models.lusid_instrument import LusidInstrument
from lusid.models.schedule import Schedule
from lusid.models.time_zone_conventions import TimeZoneConventions
from lusid.models.trading_conventions import TradingConventions

class FlexibleRepo(LusidInstrument):
    """
    Lusid representation of a repurchase agreement, where one party sells some collateral and agrees to re-buy it at a later date for some given price.  # noqa: E501
    """
    start_date: datetime = Field(description="The start date of the instrument. This is normally synonymous with the trade-date.", alias="startDate")
    maturity_date: Optional[datetime] = Field(default=None, description="The maturity date of the instrument. This is the date at which the repurchase will occur for a TermRepo.  Optional for OpenRepo, but if not provided, defaults to the StartDate plus a long period (e.g. 2099-12-31).", alias="maturityDate")
    buyer_or_seller:  StrictStr = Field(...,alias="buyerOrSeller", description="Is the user the Buyer or the Seller of this repo?  Every repo agreement has two sides, a buyer and a seller.  The Buyer pays the PurchasePrice to the Seller in exchange for legal ownership of the collateral.  At Maturity, the Buyer then receives the RepurchasePrice in exchange for returning legal ownership of the collateral.  Controls the direction of purchase and repurchase cashflows, as well as the recipient of cashflows from the collateral.    Supported string (enumeration) values are: [Buyer, Seller].") 
    repo_ccy:  StrictStr = Field(...,alias="repoCcy", description="Currency of the purchase and repurchase prices. May differ from the currencies on any collateral.") 
    repo_type:  StrictStr = Field(...,alias="repoType", description="The type of the repurchase agreement, Open or Term.  If Term, the repurchase automatically takes place at Maturity.  If Open, the agreement is rolled by the given tenor, and an interest cashflow is paid out with each roll,  unless manually triggered by a FlexibleRepoFullClosureEvent.    Supported string (enumeration) values are: [OpenRepo, TermRepo].") 
    accrual_basis:  Optional[StrictStr] = Field(None,alias="accrualBasis", description="For calculation of interest, the accrual day count to be used.  Required if no RepoRateSchedules are provided.  If both RepoRateSchedules and AccrualBasis are provided,  then AccrualBasis will take precedence.    Supported string (enumeration) values are: [Actual360, Act360, MoneyMarket, Actual365, Act365, Thirty360, ThirtyU360, Bond, ThirtyE360, EuroBond, ActualActual, ActAct, ActActIsda, ActActIsma, ActActIcma, OneOne, Act364, Act365F, Act365L, Act365_25, Act252, Bus252, NL360, NL365, ActActAFB, Act365Cad, ThirtyActIsda, Thirty365Isda, ThirtyEActIsda, ThirtyE360Isda, ThirtyE365Isda, ThirtyU360EOM].") 
    collateral: Optional[Collateral] = None
    haircut: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="Haircut on the value of the collateral, used to calculate PurchasePrice if not provided directly.  Haircut or Margin should be specified if PurchasePrice is not specified.")
    margin: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="Initial margin on the value of the collateral, used to calculate PurchasePrice if not provided directly.  Haircut or Margin should be specified if PurchasePrice is not specified.")
    open_repo_rolling_period:  Optional[StrictStr] = Field(None,alias="openRepoRollingPeriod", description="Required if the RepoType is Open.  The tenor representing the mandatory roll period if the FlexibleRepo is not manually matured.  If a user matures the FlexibleRepo via an instrument event, then the repurchase will delay until the end of this rolling period.  Generally this is set to 1D (one day), i.e. the repurchase will occur on the same day as the instrument event,  though any valid tenor is accepted with TenorUnit set to Day, Week, Month, or Year.  Note that TenorUnit T is not accepted here.") 
    purchase_price: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="The initial purchase price of the collateral.  If provided directly in this field, then Collateral.CollateralValue,  Haircut, and Margin should not be provided.", alias="purchasePrice")
    repo_rate_schedules: Optional[List[Schedule]] = Field(default=None, description="Schedules used to calculate the repurchase price and any interest payments on the FlexibleRepo.  Only one schedule may be provided, and must be of type FixedSchedule or FloatSchedule.  If RepoType is OpenRepo, a FixedSchedule or FloatSchedule must be provided to calculate the expected Repo Rate,  and RepurchasePrice must be omitted.  If RepoType is TermRepo, only one of RepurchasePrice and RepoRateSchedules should be provided.  If a RepoRateSchedule is provided on a TermRepo, the PaymentFrequency in the FlowConventions should be 1T.  StubType must be set to None, and no ExDividend configuration should be provided.", alias="repoRateSchedules")
    repurchase_price: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="The repurchase price of the repo, if known.  Only one of RepurchasePrice and RepoRateSchedules should be provided.  In the case of an OpenRepo, RepurchasePrice should not be provided,  and RepoRateSchedules should be provided instead in order to calculate the RepoRate.", alias="repurchasePrice")
    time_zone_conventions: Optional[TimeZoneConventions] = Field(default=None, alias="timeZoneConventions")
    trading_conventions: Optional[TradingConventions] = Field(default=None, alias="tradingConventions")
    is_collateral_transfer_activated: Optional[StrictBool] = Field(default=None, description="Indicates whether the FlexibleRepoCollateralTransfer event is activated.  Determines the behavior of manufactured coupons and related boolean parameters.  Defaults to false.  When true:  - Generates the FlexibleRepoCollateralTransfer event  - Processes collateral transfer transactions into holding changes  - Generates manufactured payments when due to be paid                When false:  - Does not generate the event  - Generates manufactured payments when due to be received", alias="isCollateralTransferActivated")
    instrument_type:  StrictStr = Field(...,alias="instrumentType", description="The available values are: QuotedSecurity, InterestRateSwap, FxForward, Future, ExoticInstrument, FxOption, CreditDefaultSwap, InterestRateSwaption, Bond, EquityOption, FixedLeg, FloatingLeg, BespokeCashFlowsLeg, Unknown, TermDeposit, ContractForDifference, EquitySwap, CashPerpetual, CapFloor, CashSettled, CdsIndex, Basket, FundingLeg, FxSwap, ForwardRateAgreement, SimpleInstrument, Repo, Equity, ExchangeTradedOption, ReferenceInstrument, ComplexBond, InflationLinkedBond, InflationSwap, SimpleCashFlowLoan, TotalReturnSwap, InflationLeg, FundShareClass, FlexibleLoan, UnsettledCash, Cash, MasteredInstrument, LoanFacility, FlexibleDeposit, FlexibleRepo") 
    additional_properties: Dict[str, Any] = {}
    __properties = ["instrumentType", "startDate", "maturityDate", "buyerOrSeller", "repoCcy", "repoType", "accrualBasis", "collateral", "haircut", "margin", "openRepoRollingPeriod", "purchasePrice", "repoRateSchedules", "repurchasePrice", "timeZoneConventions", "tradingConventions", "isCollateralTransferActivated"]

    @validator('instrument_type')
    def instrument_type_validate_enum(cls, value):
        """Validates the enum"""

        # Finbourne have removed enum validation on all models, except for this use case:
        # Workflow and notification application SDK use the property name 'type' as the discriminator on a number of classes.
        # During instantiation, the value of 'type' is checked against the enum values, 
        

        # check it's a class that uses the 'type' property as a discriminator
        # list of classes can be found by searching for 'actual_instance: Union[' in the generated code
        if 'FlexibleRepo' not in [ 
                                    # For notification application classes
                                    'AmazonSqsNotificationType',
                                    'AmazonSqsNotificationTypeResponse',
                                    'AmazonSqsPrincipalAuthNotificationType',
                                    'AmazonSqsPrincipalAuthNotificationTypeResponse',
                                    'AzureServiceBusTypeResponse',
                                    'AzureServiceBusNotificationType',
                                    'EmailNotificationType',
                                    'EmailNotificationTypeResponse',
                                    'SmsNotificationType',
                                    'SmsNotificationTypeResponse',
                                    'WebhookNotificationType',
                                    'WebhookNotificationTypeResponse',
                        
                                    # For workflow application classes
                                    'CreateChildTasksAction', 
                                    'RunWorkerAction', 
                                    'TriggerParentTaskAction',
                                    'CreateChildTasksActionResponse', 
                                    'RunWorkerActionResponse',
                                    'TriggerParentTaskActionResponse',
                                    'CreateNewTaskActivity',
                                    'UpdateMatchingTasksActivity',
                                    'CreateNewTaskActivityResponse', 
                                    'UpdateMatchingTasksActivityResponse',
                                    'Fail', 
                                    'GroupReconciliation', 
                                    'HealthCheck', 
                                    'LuminesceView', 
                                    'SchedulerJob', 
                                    'Sleep',
                                    'FailResponse', 
                                    'GroupReconciliationResponse', 
                                    'HealthCheckResponse', 
                                    'LuminesceViewResponse', 
                                    'SchedulerJobResponse', 
                                    'SleepResponse',
                                    'Library',
                                    'LibraryResponse',
                                    'DayRegularity',
                                    'RelativeMonthRegularity',
                                    'SpecificMonthRegularity',
                                    'WeekRegularity',
                                    'YearRegularity',
                                    'LusidEntityDataQualityCheck',
                                    'LusidEntityDataQualityCheckResponse']:
           return value
        
        # Only validate the 'type' property of the class
        if "instrument_type" != "type":
            return value

        if value not in ['QuotedSecurity', 'InterestRateSwap', 'FxForward', 'Future', 'ExoticInstrument', 'FxOption', 'CreditDefaultSwap', 'InterestRateSwaption', 'Bond', 'EquityOption', 'FixedLeg', 'FloatingLeg', 'BespokeCashFlowsLeg', 'Unknown', 'TermDeposit', 'ContractForDifference', 'EquitySwap', 'CashPerpetual', 'CapFloor', 'CashSettled', 'CdsIndex', 'Basket', 'FundingLeg', 'FxSwap', 'ForwardRateAgreement', 'SimpleInstrument', 'Repo', 'Equity', 'ExchangeTradedOption', 'ReferenceInstrument', 'ComplexBond', 'InflationLinkedBond', 'InflationSwap', 'SimpleCashFlowLoan', 'TotalReturnSwap', 'InflationLeg', 'FundShareClass', 'FlexibleLoan', 'UnsettledCash', 'Cash', 'MasteredInstrument', 'LoanFacility', 'FlexibleDeposit', 'FlexibleRepo']:
            raise ValueError("must be one of enum values ('QuotedSecurity', 'InterestRateSwap', 'FxForward', 'Future', 'ExoticInstrument', 'FxOption', 'CreditDefaultSwap', 'InterestRateSwaption', 'Bond', 'EquityOption', 'FixedLeg', 'FloatingLeg', 'BespokeCashFlowsLeg', 'Unknown', 'TermDeposit', 'ContractForDifference', 'EquitySwap', 'CashPerpetual', 'CapFloor', 'CashSettled', 'CdsIndex', 'Basket', 'FundingLeg', 'FxSwap', 'ForwardRateAgreement', 'SimpleInstrument', 'Repo', 'Equity', 'ExchangeTradedOption', 'ReferenceInstrument', 'ComplexBond', 'InflationLinkedBond', 'InflationSwap', 'SimpleCashFlowLoan', 'TotalReturnSwap', 'InflationLeg', 'FundShareClass', 'FlexibleLoan', 'UnsettledCash', 'Cash', 'MasteredInstrument', 'LoanFacility', 'FlexibleDeposit', 'FlexibleRepo')")
        return value

    class Config:
        """Pydantic configuration"""
        allow_population_by_field_name = True
        validate_assignment = True

    def __str__(self):
        """For `print` and `pprint`"""
        return pprint.pformat(self.dict(by_alias=False))

    def __repr__(self):
        """For `print` and `pprint`"""
        return self.to_str()

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

    def to_json(self) -> str:
        """Returns the JSON representation of the model using alias"""
        return json.dumps(self.to_dict())

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

    def to_dict(self):
        """Returns the dictionary representation of the model using alias"""
        _dict = self.dict(by_alias=True,
                          exclude={
                            "additional_properties"
                          },
                          exclude_none=True)
        # override the default output from pydantic by calling `to_dict()` of collateral
        if self.collateral:
            _dict['collateral'] = self.collateral.to_dict()
        # override the default output from pydantic by calling `to_dict()` of each item in repo_rate_schedules (list)
        _items = []
        if self.repo_rate_schedules:
            for _item in self.repo_rate_schedules:
                if _item:
                    _items.append(_item.to_dict())
            _dict['repoRateSchedules'] = _items
        # override the default output from pydantic by calling `to_dict()` of time_zone_conventions
        if self.time_zone_conventions:
            _dict['timeZoneConventions'] = self.time_zone_conventions.to_dict()
        # override the default output from pydantic by calling `to_dict()` of trading_conventions
        if self.trading_conventions:
            _dict['tradingConventions'] = self.trading_conventions.to_dict()
        # puts key-value pairs in additional_properties in the top level
        if self.additional_properties is not None:
            for _key, _value in self.additional_properties.items():
                _dict[_key] = _value

        # set to None if maturity_date (nullable) is None
        # and __fields_set__ contains the field
        if self.maturity_date is None and "maturity_date" in self.__fields_set__:
            _dict['maturityDate'] = None

        # set to None if accrual_basis (nullable) is None
        # and __fields_set__ contains the field
        if self.accrual_basis is None and "accrual_basis" in self.__fields_set__:
            _dict['accrualBasis'] = None

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

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

        # set to None if open_repo_rolling_period (nullable) is None
        # and __fields_set__ contains the field
        if self.open_repo_rolling_period is None and "open_repo_rolling_period" in self.__fields_set__:
            _dict['openRepoRollingPeriod'] = None

        # set to None if purchase_price (nullable) is None
        # and __fields_set__ contains the field
        if self.purchase_price is None and "purchase_price" in self.__fields_set__:
            _dict['purchasePrice'] = None

        # set to None if repo_rate_schedules (nullable) is None
        # and __fields_set__ contains the field
        if self.repo_rate_schedules is None and "repo_rate_schedules" in self.__fields_set__:
            _dict['repoRateSchedules'] = None

        # set to None if repurchase_price (nullable) is None
        # and __fields_set__ contains the field
        if self.repurchase_price is None and "repurchase_price" in self.__fields_set__:
            _dict['repurchasePrice'] = None

        return _dict

    @classmethod
    def from_dict(cls, obj: dict) -> FlexibleRepo:
        """Create an instance of FlexibleRepo from a dict"""
        if obj is None:
            return None

        if not isinstance(obj, dict):
            return FlexibleRepo.parse_obj(obj)

        _obj = FlexibleRepo.parse_obj({
            "instrument_type": obj.get("instrumentType"),
            "start_date": obj.get("startDate"),
            "maturity_date": obj.get("maturityDate"),
            "buyer_or_seller": obj.get("buyerOrSeller"),
            "repo_ccy": obj.get("repoCcy"),
            "repo_type": obj.get("repoType"),
            "accrual_basis": obj.get("accrualBasis"),
            "collateral": Collateral.from_dict(obj.get("collateral")) if obj.get("collateral") is not None else None,
            "haircut": obj.get("haircut"),
            "margin": obj.get("margin"),
            "open_repo_rolling_period": obj.get("openRepoRollingPeriod"),
            "purchase_price": obj.get("purchasePrice"),
            "repo_rate_schedules": [Schedule.from_dict(_item) for _item in obj.get("repoRateSchedules")] if obj.get("repoRateSchedules") is not None else None,
            "repurchase_price": obj.get("repurchasePrice"),
            "time_zone_conventions": TimeZoneConventions.from_dict(obj.get("timeZoneConventions")) if obj.get("timeZoneConventions") is not None else None,
            "trading_conventions": TradingConventions.from_dict(obj.get("tradingConventions")) if obj.get("tradingConventions") is not None else None,
            "is_collateral_transfer_activated": obj.get("isCollateralTransferActivated")
        })
        # store additional fields in additional_properties
        for _key in obj.keys():
            if _key not in cls.__properties:
                _obj.additional_properties[_key] = obj.get(_key)

        return _obj

FlexibleRepo.update_forward_refs()
