from datetime import datetime
from pydantic_xml import BaseXmlModel, element, attr, computed_element

from bb_integrations_lib.models.fuelquest.shared import (
    Carrier,
    Buyer,
    OrganizationId,
    CountryCode,
    identTypeFQ,
    FQ_DATE_FORMAT,
    FQ_TIME_FORMAT,
    ReportType,
    identTypeFQ_str,
    AllowanceOrChargeReason,
    UOMBasis,
    ReleaseNumber,
)


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

    value: str


class LineItemFuelProductQty(BaseXmlModel):
    uom_basis: UOMBasis = attr(name="UOMBasis")

    value: float


class LineItemTankMeasurement(BaseXmlModel):
    uom_basis: UOMBasis | None = attr(name="UOMBasis", default=None)
    beginning: float | None = attr(name="beginning", default=None)
    ending: float | None = attr(name="ending", default=None)
    type: str | None = attr(name="type", default=None)


class LineItemTank(BaseXmlModel):
    ident: str = attr(name="ident")


class LineItemAllowanceOrCharge(BaseXmlModel):
    allowance_or_charge_reason: AllowanceOrChargeReason = element(
        tag="AllowanceOrChargeReason"
    )
    charge_amt: float = element(tag="ChargeAmt")


class LineItem(BaseXmlModel):
    count: int = attr(name="count")
    tank: LineItemTank = element(tag="Tank")

    fuel_product_id: LineItemTankFuelProductId = element(tag="FuelProductId")
    fuel_product_qty: LineItemFuelProductQty = element(tag="FuelProductQty")
    tank_measurement: LineItemTankMeasurement = element(tag="TankMeasurement")

    allowance_or_charge: LineItemAllowanceOrCharge = element(tag="AllowanceOrCharge")


class DeliveredTo(BaseXmlModel):
    ident_type: identTypeFQ = attr(name="identType", default=identTypeFQ_str)
    ident: str = attr(name="ident")
    diversion: str | None = attr(name="diversion", default="")

    # Contact details
    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)
    organization_id: OrganizationId = element(tag="OrganizationId")
    start_delivery_datetime: datetime = element(exclude=True)

    @computed_element(tag="StartDeliveryDate")
    def start_delivery_date(self) -> str:
        return self.start_delivery_datetime.strftime(FQ_DATE_FORMAT)

    @computed_element(tag="StartDeliveryTime")
    def start_delivery_time(self) -> str:
        return self.start_delivery_datetime.strftime(FQ_TIME_FORMAT).replace(
            "+00:00", "-00:00"
        )

    end_delivery_datetime: datetime = element(exclude=True)

    @computed_element(tag="EndDeliveryDate")
    def end_delivery_date(self) -> str:
        return self.end_delivery_datetime.strftime(FQ_DATE_FORMAT)

    @computed_element(tag="EndDeliveryTime")
    def end_delivery_time(self) -> str:
        return self.end_delivery_datetime.strftime(FQ_TIME_FORMAT).replace(
            "+00:00", "-00:00"
        )

    carrier_way_bill_number: str | None = element(
        tag="CarrierWayBillNumber", default=None
    )

    line_items: list[LineItem] = element(tag="LineItem")


class DeliveryInformation(BaseXmlModel):
    delivered_to: DeliveredTo = element(tag="DeliveredTo")


class DeliveryEquipment(BaseXmlModel):
    # TODO
    pass


class ContactName(BaseXmlModel):
    first_name: str | None = element(tag="FirstName")
    last_name: str | None = element(tag="LastName")


class ConsignorContact(BaseXmlModel):
    name: ContactName = element(tag="Name")


class Consignor(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)
    contact: ConsignorContact | None = element(tag="Contact", default=None)
    organization_id: OrganizationId = element(tag="OrganizationId")
    # TODO: Do we need a PDXId type of its own?
    pdx_id: OrganizationId | None = element(tag="PDXId", default=None)


class BillToContact(BaseXmlModel):
    name: ContactName = element(tag="Name")


class BillTo(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)
    contact: BillToContact | None = element(tag="Contact", default=None)
    organization_id: OrganizationId = element(tag="OrganizationId")


class DeliveryConfirmation(BaseXmlModel):
    confirmation_type: ReportType = attr(name="confirmationType")

    carrier: Carrier = element(tag="Carrier")
    buyer: Buyer = element(tag="Buyer")
    equipment: DeliveryEquipment = element(tag="Equipment")
    consignor: Consignor = element(tag="Consignor")
    bill_to: BillTo | None = element(tag="BillTo", default=None)
    bill_of_lading_number: str = element(tag="BillOfLadingNumber")
    purchase_order_number: str = element(tag="PurchaseOrderNumber")
    release_number: ReleaseNumber = element(tag="ReleaseNumber")

    delivery_information: DeliveryInformation = element(tag="DeliveryInformation")
