from typing import Self

from datetime import date, datetime
from enum import StrEnum
from pydantic import model_validator

from pydantic_xml import BaseXmlModel, element, attr

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


class TermsType(StrEnum):
    BLANK = ""


class Terms(BaseXmlModel):
    terms_type: TermsType = element(tag="TermsType", default=TermsType.BLANK)


class InvoiceLocation(BaseXmlModel):
    name: str = element(tag="Name")
    state: str = element(tag="State")
    country_code: CountryCode = element(tag="CountryCode")
    organization_id: OrganizationId = element(tag="OrganizationId")


class InvoiceSupplier(BaseXmlModel):
    name: str | None = element(tag="Name", default=None)
    organization_id: OrganizationId = element(tag="OrganizationId")


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


class RateDistance(BaseXmlModel):
    uom_basis: UOMBasis = attr(name="UOMBasis", default=None)


class InvoiceAllowanceOrCharge(BaseXmlModel):
    reason: AllowanceOrChargeReason = element(tag="AllowanceOrChargeReason")
    charge_amt: float = element(tag="ChargeAmt")


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


class FuelProductQty(BaseXmlModel):
    uom_basis: str = attr(name="UOMBasis")
    quantity: float


class ProductDetail(BaseXmlModel):
    fuel_product_id: FuelProductId = element(tag="FuelProductId")
    fuel_product_qty: FuelProductQty = element(tag="FuelProductQty")


class FreightCharge(BaseXmlModel):
    delivered_qty: TwoDigitFloat = attr(name="deliveredQty")
    rated_qty: TwoDigitFloat = attr(name="ratedQty")
    rate: float = attr(name="rate", default=None)
    rate_basis: str = attr(name="rateBasis", default=None)
    currency: str = attr(name="currency")
    amount: float


class FreightDetail(BaseXmlModel):
    count: int = attr("count")
    freight_charge: FreightCharge = element(tag="FreightCharge")
    allowance_or_charge: InvoiceAllowanceOrCharge | None = element(
        tag="AllowanceOrCharge", default=None
    )
    product_details: list[ProductDetail] = element(tag="ProductDetail")


class FreightInvoiceDetail(BaseXmlModel):
    location: InvoiceLocation = element(tag="Location")
    supplier: InvoiceSupplier = element(tag="Supplier")
    terminal: SupplyTerminal = element(tag="Terminal")
    release_number: ReleaseNumber = element(tag="ReleaseNumber")
    bill_of_lading_number: str = element(tag="BillOfLadingNumber")
    carrier_way_bill_number: str = element(tag="CarrierWayBillNumber")
    rate_reference: RateReference = element(tag="RateReference")
    rate_distance: RateDistance = element(tag="RateDistance")

    loading_datetime: datetime = element(exclude=True)
    loading_date: str = element(tag="LoadingDate", default=None)
    loading_time: str = element(tag="LoadingTime", default=None)

    @model_validator(mode="after")
    def populate_loading_dt(self) -> Self:
        self.loading_date = self.loading_datetime.strftime(FQ_DATE_FORMAT)
        self.loading_time = self.loading_datetime.strftime(FQ_TIME_FORMAT).replace(
            "+00:00", "-00:00"
        )
        return self

    delivery_datetime: datetime = element(exclude=True)
    delivery_date: str = element(tag="DeliveryDate", default=None)
    delivery_time: str = element(tag="DeliveryTime", default=None)

    @model_validator(mode="after")
    def populate_delivery_dt(self) -> Self:
        self.delivery_date = self.delivery_datetime.strftime(FQ_DATE_FORMAT)
        self.delivery_time = self.delivery_datetime.strftime(FQ_TIME_FORMAT).replace(
            "+00:00", "-00:00"
        )
        return self

    freight_details: list[FreightDetail] = element(tag="FreightDetail")


class AdditionalAllowancesAndCharges(BaseXmlModel):
    total_additional_net: float = element(
        tag="TotalAdditionalAllowancesAndChargesNetAmt"
    )


class FreightInvTotalsDetails(BaseXmlModel):
    count: int = attr(name="count")


class TotalFreightInvoiceDueAmt(BaseXmlModel):
    ident_type: str = attr(name="identType", default="")
    amount: float


class FreightInvoiceTotals(BaseXmlModel):
    freight_details: FreightInvTotalsDetails = element(tag="FreightDetails")
    additional_allowances_and_charges: AdditionalAllowancesAndCharges = element(
        tag="AdditionalAllowancesAndCharges"
    )
    total_due_amount: TotalFreightInvoiceDueAmt = element(
        tag="TotalFreightInvoiceDueAmt"
    )


class FreightInvoiceSummary(BaseXmlModel):
    totals: FreightInvoiceTotals = element(tag="FreightInvoiceTotals")


class FuelFreightInvoice(BaseXmlModel):
    invoice_type: ReportType = attr(name="invoiceType")

    carrier: Carrier = element(tag="Carrier")
    buyer: Buyer = element(tag="Buyer")
    invoice_number: str = element(tag="InvoiceNumber")
    invoice_date: date = element(tag="InvoiceDate")

    freight_invoice_detail: list[FreightInvoiceDetail] = element(
        tag="FreightInvoiceDetail"
    )
    freight_invoice_summary: FreightInvoiceSummary = element(
        tag="FreightInvoiceSummary"
    )

    terms: Terms = element(tag="Terms")
