import xml.etree.ElementTree as ET
from xml.dom import minidom
from ...models.models import DevisData


def generate_xml(data):
    validated = DevisData.model_validate(data)
    NS = {
        "rsm": "urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100",
        "ram": "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100",
        "udt": "urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"
    }

    root = ET.Element("rsm:CrossIndustryInvoice", {
        "xmlns:rsm": NS["rsm"],
        "xmlns:ram": NS["ram"],
        "xmlns:udt": NS["udt"]
    })

    # ================= CONTEXT =================
    ctx = ET.SubElement(root, "rsm:ExchangedDocumentContext")

    bp = ET.SubElement(ctx, "ram:BusinessProcessSpecifiedDocumentContextParameter")
    ET.SubElement(bp, "ram:ID").text = ""

    guideline = ET.SubElement(ctx, "ram:GuidelineSpecifiedDocumentContextParameter")
    ET.SubElement(guideline, "ram:ID").text = "urn:cen.eu:en16931:2017"

    # ================= DOCUMENT =================
    doc = ET.SubElement(root, "rsm:ExchangedDocument")
    ET.SubElement(doc, "ram:ID").text = data["invoice"]["order"]
    ET.SubElement(doc, "ram:TypeCode").text = "380"

    issue = ET.SubElement(doc, "ram:IssueDateTime")
    date = ET.SubElement(issue, "udt:DateTimeString", {"format": "102"})
    date.text = data["invoice"]["date"]  # YYYYMMDD

    note = ET.SubElement(doc, "ram:IncludedNote")
    ET.SubElement(note, "ram:Content").text = data["invoice"].get("note", "")
    ET.SubElement(note, "ram:SubjectCode").text = ""

    # ================= TRANSACTION =================
    sctt = ET.SubElement(root, "rsm:SupplyChainTradeTransaction")

    # ================= LINES =================
    for i, line in enumerate(data["invoice_lines"], start=1):
        item = ET.SubElement(sctt, "ram:IncludedSupplyChainTradeLineItem")

        doc_line = ET.SubElement(item, "ram:AssociatedDocumentLineDocument")
        ET.SubElement(doc_line, "ram:LineID").text = str(i)

        product = ET.SubElement(item, "ram:SpecifiedTradeProduct")
        ET.SubElement(product, "ram:SellerAssignedID").text = line.get("seller_id", "")
        ET.SubElement(product, "ram:BuyerAssignedID").text = line.get("buyer_id", "")
        ET.SubElement(product, "ram:Name").text = line["product_description"]

        agreement = ET.SubElement(item, "ram:SpecifiedLineTradeAgreement")
        price = ET.SubElement(agreement, "ram:NetPriceProductTradePrice")
        ET.SubElement(price, "ram:ChargeAmount", {"currencyID": "EUR"}).text = f"{line['unit_price']:.2f}"

        delivery = ET.SubElement(item, "ram:SpecifiedLineTradeDelivery")
        ET.SubElement(
            delivery,
            "ram:BilledQuantity",
            {"unitCode": line.get("unit", "C62")}
        ).text = str(line["quantity"])

        settlement = ET.SubElement(item, "ram:SpecifiedLineTradeSettlement")
        tax = ET.SubElement(settlement, "ram:ApplicableTradeTax")
        ET.SubElement(tax, "ram:TypeCode").text = "VAT"
        ET.SubElement(tax, "ram:CategoryCode").text = "S"
        ET.SubElement(tax, "ram:RateApplicablePercent").text = str(line["tva_code"])

        total_line = line["quantity"] * line["unit_price"]
        summation = ET.SubElement(
            settlement,
            "ram:SpecifiedTradeSettlementLineMonetarySummation"
        )
        ET.SubElement(
            summation,
            "ram:LineTotalAmount",
            {"currencyID": "EUR"}
        ).text = f"{total_line:.2f}"

    # ================= HEADER AGREEMENT =================
    agreement = ET.SubElement(sctt, "ram:ApplicableHeaderTradeAgreement")

    ET.SubElement(agreement, "ram:BuyerReference").text = data.get("buyer_reference", "")

    def build_party(parent, tag, party):
        p = ET.SubElement(parent, f"ram:{tag}")
        ET.SubElement(p, "ram:ID").text = party.get("id", "")
        ET.SubElement(p, "ram:Name").text = party["name"]

        addr = ET.SubElement(p, "ram:PostalTradeAddress")
        ET.SubElement(addr, "ram:PostcodeCode").text = party["address"][0]
        ET.SubElement(addr, "ram:LineOne").text = party["address"][1]
        ET.SubElement(addr, "ram:CityName").text = ""
        ET.SubElement(addr, "ram:CountryID").text = ""

        tax = ET.SubElement(p, "ram:SpecifiedTaxRegistration")
        ET.SubElement(tax, "ram:ID").text = party.get("vat", "")

    build_party(agreement, "SellerTradeParty", data["company"])
    build_party(agreement, "BuyerTradeParty", data["partner"])

    # ================= DELIVERY =================
    delivery = ET.SubElement(sctt, "ram:ApplicableHeaderTradeDelivery")

    # ================= SETTLEMENT =================
    settlement = ET.SubElement(sctt, "ram:ApplicableHeaderTradeSettlement")
    ET.SubElement(settlement, "ram:InvoiceCurrencyCode").text = "EUR"

    net = sum(l["quantity"] * l["unit_price"] for l in data["invoice_lines"])
    vat = sum((l["quantity"] * l["unit_price"]) * l["tva_code"] / 100 for l in data["invoice_lines"])

    tax = ET.SubElement(settlement, "ram:ApplicableTradeTax")
    ET.SubElement(tax, "ram:CalculatedAmount", {"currencyID": "EUR"}).text = f"{vat:.2f}"
    ET.SubElement(tax, "ram:TypeCode").text = "VAT"
    ET.SubElement(tax, "ram:BasisAmount", {"currencyID": "EUR"}).text = f"{net:.2f}"
    ET.SubElement(tax, "ram:CategoryCode").text = "S"
    ET.SubElement(tax, "ram:RateApplicablePercent").text = str(data["invoice_lines"][0]["tva_code"])

    totals = ET.SubElement(
        settlement,
        "ram:SpecifiedTradeSettlementHeaderMonetarySummation"
    )
    ET.SubElement(totals, "ram:LineTotalAmount", {"currencyID": "EUR"}).text = f"{net:.2f}"
    ET.SubElement(totals, "ram:TaxBasisTotalAmount", {"currencyID": "EUR"}).text = f"{net:.2f}"
    ET.SubElement(totals, "ram:TaxTotalAmount", {"currencyID": "EUR"}).text = f"{vat:.2f}"
    ET.SubElement(totals, "ram:GrandTotalAmount", {"currencyID": "EUR"}).text = f"{net + vat:.2f}"
    ET.SubElement(totals, "ram:DuePayableAmount", {"currencyID": "EUR"}).text = f"{net + vat:.2f}"

    xml = ET.tostring(root, encoding="utf-8")
    return minidom.parseString(xml).toprettyxml(indent="  ", encoding="utf-8").decode()

import os

def write_xml_from_string(xml_string: str, destination_folder: str, file_name: str):
    """
    Writes an XML file from an already well-formed XML string.

    :param xml_string: String containing the full XML content
    :param destination_folder: Destination directory
    :param file_name: File name (with or without .xml extension)
    """

    # Add .xml extension if missing
    if not file_name.endswith(".xml"):
        file_name += ".xml"

    # Create destination folder if it does not exist
    os.makedirs(destination_folder, exist_ok=True)

    # Full file path
    file_path = os.path.join(destination_folder, file_name)

    # Write XML to file
    with open(file_path, "w", encoding="utf-8") as file:
        file.write(xml_string)

    return file_path
