from datetime import datetime, date, time
from enum import StrEnum

from pydantic.functional_serializers import PlainSerializer
from pydantic_xml import BaseXmlModel, element, attr, computed_attr
from typing import Literal, Annotated

identTypeFQ = Literal["FuelQuestID"]
identTypeFQ_str = "FuelQuestID"
FQ_DATE_FORMAT = "%Y-%m-%d"
FQ_TIME_FORMAT = "%H:%M:%S%:z"

FuelQuestDate = Annotated[date, PlainSerializer(lambda x: x.strftime(FQ_DATE_FORMAT))]
FuelQuestTime = Annotated[
    time,
    PlainSerializer(lambda x: x.strftime(FQ_TIME_FORMAT).replace("+00:00", "-00:00")),
]


def serialize_two_digit_float(value: float) -> str:
    return f"{value:.2f}"


TwoDigitFloat = Annotated[float, PlainSerializer(serialize_two_digit_float)]


class CountryCode(BaseXmlModel):
    code: str = attr(name="code")


class OrganizationId(BaseXmlModel):
    ident_type: identTypeFQ = attr(
        name="identType", frozen=True, default=identTypeFQ_str
    )
    ident: str = attr(name="ident")


class ReleaseNumber(BaseXmlModel):
    expiration_datetime: datetime | None = element(exclude=True)

    @computed_attr(name="expirationDate")
    def expiration_date(self) -> str:
        if self.expiration_datetime:
            return self.expiration_datetime.strftime(FQ_DATE_FORMAT)
        else:
            return ""

    @computed_attr(name="expirationTime")
    def expiration_time(self) -> str:
        if self.expiration_datetime:
            return self.expiration_datetime.strftime(FQ_TIME_FORMAT).replace(
                "+00:00", "-00:00"
            )
        else:
            return ""

    value: str


class TruckTerminal(BaseXmlModel):
    ident_type: identTypeFQ = attr(
        name="identType", frozen=True, default=identTypeFQ_str
    )

    @computed_attr(name="ident")
    def ident(self) -> str:
        return self.organization_id.ident

    name: str = element(tag="Name")
    address: str | None = element(tag="Address", default=None)
    city: str | None = element(tag="City", default=None)
    state: str | None = element(tag="State", default=None)
    postal_code: str | None = element(tag="PostalCode", default=None)
    country_code: CountryCode | None = element(tag="CountryCode", default=None)
    fax: str | None = element(tag="Fax", default=None)
    email: str | None = element(tag="Email", default=None)
    organization_id: OrganizationId = element(tag="OrganizationId")


class SupplyTerminal(BaseXmlModel):
    ident_type: identTypeFQ = attr(
        name="identType", frozen=True, default=identTypeFQ_str
    )

    @computed_attr(name="ident")
    def ident(self) -> str:
        return self.organization_id.ident

    name: str = element(tag="Name")
    address: str | None = element(tag="Address", default=None)
    city: str | None = element(tag="City", default=None)
    state: str | None = element(tag="State", default=None)
    postal_code: str | None = element(tag="PostalCode", default=None)
    country_code: CountryCode | None = element(tag="CountryCode", default=None)
    fax: str | None = element(tag="Fax", default=None)
    email: str | None = element(tag="Email", default=None)
    organization_id: OrganizationId = element(tag="OrganizationId")


class Driver(BaseXmlModel):
    identType: identTypeFQ = attr(
        name="identType", frozen=True, default=identTypeFQ_str
    )
    ident: str = attr(name="ident")

    issuer: str = attr(name="issuer")


class Carrier(BaseXmlModel):
    name: str = element(tag="Name")
    address: str | None = element(tag="Address", default=None)
    city: str | None = element(tag="City", default=None)
    state: str = element(tag="State")
    postal_code: str | None = element(tag="PostalCode", default=None)
    country_code: CountryCode = element(tag="CountryCode")
    fax: str | None = element(tag="Fax", default=None)
    email: str | None = element(tag="Email", default=None)
    truck_terminal: TruckTerminal = element(tag="TruckTerminal")
    organization_id: OrganizationId = element(tag="OrganizationId")
    scac: str | None = element(tag="SCAC", default=None)


class Buyer(BaseXmlModel):
    name: str | None = element(tag="Name", default=None)
    phone: str | None = element(tag="Phone", default=None)
    state: str = element(tag="State")
    country_code: CountryCode = element(tag="CountryCode", default=None)
    organization_id: OrganizationId = element(tag="OrganizationId")


class ReportType(StrEnum):
    ORIGINAL = "original"
    UPDATE = "update"


class AllowanceOrChargeReasonValues(StrEnum):
    FREIGHT = "Freight"
    DEMURRAGE = "Demurrage"
    FUEL_SURCHARGE = "Fuel Surcharge"
    MULTI_SOURCE_LOAD_FEE = "Multi-Source Load Fee"
    MULTI_DESTINATION_UNLOAD_FEE = "Multi-Destination Unload Fee"
    INSURANCE_FEE = "Insurance Fee"
    TOLL_CHARGES = "Toll Charges"
    OTHER_CHARGES = "Other Charges"
    CHARGEABLE_LOADING_STOPS = "Chargeable Loading Stops"
    CHARGEABLE_UNLOADING_STOPS = "Chargeable Unloading Stops"


class AllowanceOrChargeReason(BaseXmlModel):
    quantity: TwoDigitFloat = attr(name="quantity")

    reason: AllowanceOrChargeReasonValues


class UOMBasis(StrEnum):
    GROSS_GALLONS = "GrossGallons"
    GROSS_LITERS = "GrossLiters"
    NET_GALLONS = "NetGallons"
    NET_LITERS = "NetLiters"
    BLANK = ""
