from dataclasses import dataclass, field
from decimal import Decimal
from typing import List, Optional

from ..validations import validate_decimal, validate_list
from .flex_message import FlexMessage
from .payload_message import PayloadMessageResponse


@dataclass(kw_only=True)
class FlexOfferOptionISP:
    """
    :ivar power: Power specified for this ISP in Watts. Also see the
        important notes about the sign of this attribute in the main
        documentation entry for the ISP element.
    :ivar start: Number of the first ISPs this element refers to. The
        first ISP of a day has number 1.
    :ivar duration: The number of the ISPs this element represents.
        Optional, default value is 1.
    """
    class Meta:
        name = "FlexOfferOptionISP"

    power: int = field(
        metadata={
            "name": "Power",
            "type": "Attribute",
            "required": True,
        }
    )
    start: int = field(
        metadata={
            "name": "Start",
            "type": "Attribute",
            "required": True,
        }
    )
    duration: int = field(
        default=1,
        metadata={
            "name": "Duration",
            "type": "Attribute",
        }
    )


@dataclass(kw_only=True)
class FlexOfferOption:
    """
    :ivar isp:
    :ivar option_reference: The identification of this option.
    :ivar price: The asking price for the flexibility offered in this
        option.
    :ivar min_activation_factor: The minimal activation factor for this
        OfferOption. An AGR may choose to include MinActivationFactor in
        FlexOffers even if the DSO is not interested in partial
        activation. In that case the DSO will simply use an
        ActivationFactor of 1.00 in every FlexOrder.
    """
    isps: List[FlexOfferOptionISP] = field(
        default_factory=list,
        metadata={
            "name": "ISP",
            "type": "Element",
            "min_occurs": 1,
        }
    )
    option_reference: str = field(
        metadata={
            "name": "OptionReference",
            "type": "Attribute",
            "required": True,
        }
    )
    price: Decimal = field(
        metadata={
            "name": "Price",
            "type": "Attribute",
            "required": True,
            "fraction_digits": 4,
        }
    )
    min_activation_factor: Decimal = field(
        default=Decimal("1.00"),
        metadata={
            "name": "MinActivationFactor",
            "type": "Attribute",
            "min_inclusive": Decimal("0.01"),
            "max_inclusive": Decimal("1.00"),
            "fraction_digits": 2,
        }
    )

    def __post_init__(self):
        validate_list('isps', self.isps, FlexOfferOptionISP, 1)
        self.price = validate_decimal('price', self.price, 4)
        self.min_activation_factor = validate_decimal('min_activation_factor', self.min_activation_factor, 2)


@dataclass(kw_only=True)
class FlexOfferResponse(PayloadMessageResponse):
    flex_offer_message_id: str = field(
        metadata={
            "name": "FlexOfferMessageID",
            "type": "Attribute",
            "required": True,
            "pattern": r"[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}",
        }
    )


@dataclass(kw_only=True)
class FlexOffer(FlexMessage):
    """
    :ivar offer_option:
    :ivar expiration_date_time: Date and time, including the time zone
        (ISO 8601 formatted as per http://www.w3.org/TR/NOTE-datetime)
        until which the FlexOffer is valid.
    :ivar flex_request_message_id: MessageID of the FlexRequest message
        this request is based on. Mandatory if and only if solicited.
    :ivar contract_id: Reference to the concerning contract, if
        applicable. The contract may be either bilateral or commoditized
        market contract.
    :ivar d_prognosis_message_id: MessageID of the D-Prognosis this
        request is based on, if it has been agreed that the baseline is
        based on D-prognoses.
    :ivar baseline_reference: Identification of the baseline prognosis,
        if another baseline methodology is used than based on
        D-prognoses
    :ivar currency: ISO 4217 code indicating the currency that applies
        to the price of the FlexOffer.
    """
    offer_options: List[FlexOfferOption] = field(
        default_factory=list,
        metadata={
            "name": "OfferOption",
            "type": "Element",
            "min_occurs": 1,
        }
    )
    expiration_date_time: str = field(
        metadata={
            "name": "ExpirationDateTime",
            "type": "Attribute",
            "required": True,
            "pattern": r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{0,9})?([+-]\d{2}:\d{2}|Z)",
        }
    )
    unsolicited: Optional[bool] = field(
        default=None,
        metadata={
            "name": "Unsolicited",
            "type": "Attribute",
        }
    )
    flex_request_message_id: Optional[str] = field(
        default=None,
        metadata={
            "name": "FlexRequestMessageID",
            "type": "Attribute",
            "pattern": r"[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}",
        }
    )
    contract_id: Optional[str] = field(
        default=None,
        metadata={
            "name": "ContractID",
            "type": "Attribute",
        }
    )
    d_prognosis_message_id: Optional[str] = field(
        default=None,
        metadata={
            "name": "D-PrognosisMessageID",
            "type": "Attribute",
            "pattern": r"[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}",
        }
    )
    baseline_reference: Optional[str] = field(
        default=None,
        metadata={
            "name": "BaselineReference",
            "type": "Attribute",
        }
    )
    currency: str = field(
        default="EUR",
        metadata={
            "name": "Currency",
            "type": "Attribute",
            "required": True,
            "pattern": r"[A-Z]{3}",
        }
    )

    def __post_init__(self):
        validate_list('offer_options', self.offer_options, FlexOfferOption, 1)
        if not self.unsolicited and self.flex_request_message_id is None:
            raise TypeError("FlexRequestMessageId is required if Unsolicited is not True")
