namespace DXApp.TemplateKitProject.Services;

using DXApp.TemplateKitProject.Models;
using System.Xml.Linq;

public class ZugferdParserService
{
    // ZUGFeRD v1 Namespace
    private static readonly XNamespace RsmV1 = "urn:un:unece:uncefact:data:standard:CBFBUY:5";

    // ZUGFeRD v2 / Factur-X Namespaces
    private static readonly XNamespace RsmV2 = "urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100";

    private static readonly XNamespace Ram = "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100";
    private static readonly XNamespace Udt = "urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100";

    // ZUGFeRD v1.0 FeRD (CrossIndustryDocument)
    private static readonly XNamespace RsmFerd1 = "urn:ferd:CrossIndustryDocument:invoice:1p0";

    private static readonly XNamespace RamV12 = "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:12";
    private static readonly XNamespace UdtV15 = "urn:un:unece:uncefact:data:standard:UnqualifiedDataType:15";

    // NACHHER:
    public ZugferdInvoice Parse(string xml)
    {
        var doc = XDocument.Parse(xml);
        var root = doc.Root!;

        if (root.Name.Namespace == RsmV1) return ParseV1(root, xml);
        if (root.Name.Namespace == RsmFerd1) return ParseV1Ferd(root, xml);
        return ParseV2(root, xml);
    }

    // ── ZUGFeRD v1 (CBFBUY:5) ────────────────────────────────────────────────
    private static ZugferdInvoice ParseV1(XElement root, string xml)
    {
        // In v1 haben Kind-Elemente KEINEN Namespace-Präfix
        var ns = XNamespace.None;
        var rsm = RsmV1;

        var header = root.Element(rsm + "HeaderExchangedDocument");
        var trade = root.Element(rsm + "SpecifiedSupplyChainTradeTransaction");
        var agreement = trade?.Element(ns + "ApplicableSupplyChainTradeAgreement");
        var settlement = trade?.Element(ns + "ApplicableSupplyChainTradeSettlement");

        var seller = agreement?.Element(ns + "SellerTradeParty");
        var buyer = agreement?.Element(ns + "BuyerTradeParty");
        var summation = settlement?.Element(ns + "SpecifiedTradeSettlementMonetarySummation");
        var payment = settlement?.Element(ns + "SpecifiedTradeSettlementPaymentMeans");

        // SellerTaxId: das SpecifiedTaxRegistration mit schemeID="VA" nehmen
        var sellerTaxId = seller?
            .Elements(ns + "SpecifiedTaxRegistration")
            .FirstOrDefault(e => (string?)e.Element(ns + "ID")?.Attribute("schemeID") == "VA")
            ?.Element(ns + "ID")?.Value ?? string.Empty;

        return new ZugferdInvoice
        {
            InvoiceNumber = header?.Element(ns + "ID")?.Value ?? string.Empty,
            InvoiceDate = ParseDate(header?.Element(ns + "IssueDateTime")?.Value),
            SellerName = seller?.Element(ns + "Name")?.Value ?? string.Empty,
            SellerTaxId = sellerTaxId,
            BuyerName = buyer?.Element(ns + "Name")?.Value ?? string.Empty,
            CurrencyCode = settlement?.Element(ns + "InvoiceCurrencyCode")?.Value ?? "EUR",
            TotalAmount = ParseDecimal(summation?.Element(ns + "GrandTotalAmount")?.Value),
            TaxAmount = ParseDecimal(summation?.Element(ns + "TaxTotalAmount")?.Value),
            Iban = payment?.Element(ns + "PayeePartyCreditorFinancialAccount")
                                    ?.Element(ns + "IBANID")?.Value ?? string.Empty,
            RawXml = xml,
            ImportedAt = DateTime.UtcNow
        };
    }

    // ── ZUGFeRD v2 / Factur-X ────────────────────────────────────────────────
    private static ZugferdInvoice ParseV2(XElement root, string xml)
    {
        var header = root.Element(RsmV2 + "ExchangedDocument");
        var trade = root.Element(RsmV2 + "SupplyChainTradeTransaction");
        var agreement = trade?.Element(Ram + "ApplicableHeaderTradeAgreement");
        var settlement = trade?.Element(Ram + "ApplicableHeaderTradeSettlement");

        return new ZugferdInvoice
        {
            InvoiceNumber = header?.Element(Ram + "ID")?.Value ?? string.Empty,
            InvoiceDate = ParseDate(header?.Element(Ram + "IssueDateTime")
                                             ?.Element(Udt + "DateTimeString")?.Value),
            SellerName = agreement?.Element(Ram + "SellerTradeParty")
                                      ?.Element(Ram + "Name")?.Value ?? string.Empty,
            SellerTaxId = agreement?.Element(Ram + "SellerTradeParty")
                                      ?.Element(Ram + "SpecifiedTaxRegistration")
                                      ?.Element(Ram + "ID")?.Value ?? string.Empty,
            BuyerName = agreement?.Element(Ram + "BuyerTradeParty")
                                      ?.Element(Ram + "Name")?.Value ?? string.Empty,
            CurrencyCode = settlement?.Element(Ram + "InvoiceCurrencyCode")?.Value ?? "EUR",
            TotalAmount = ParseDecimal(settlement
                                ?.Element(Ram + "SpecifiedTradeSettlementHeaderMonetarySummation")
                                ?.Element(Ram + "GrandTotalAmount")?.Value),
            TaxAmount = ParseDecimal(settlement
                                ?.Element(Ram + "SpecifiedTradeSettlementHeaderMonetarySummation")
                                ?.Element(Ram + "TaxTotalAmount")?.Value),
            Iban = settlement?.Element(Ram + "SpecifiedTradeSettlementPaymentMeans")
                                       ?.Element(Ram + "PayeePartyCreditorFinancialAccount")
                                       ?.Element(Ram + "IBANID")?.Value ?? string.Empty,
            RawXml = xml,
            ImportedAt = DateTime.UtcNow
        };
    }

    // ── ZUGFeRD v1.0 FeRD (CrossIndustryDocument:invoice:1p0) ───────────────────
    private static ZugferdInvoice ParseV1Ferd(XElement root, string xml)
    {
        var rsm = RsmFerd1;
        var ram = RamV12;
        var udt = UdtV15;

        var header = root.Element(rsm + "HeaderExchangedDocument");
        var trade = root.Element(rsm + "SpecifiedSupplyChainTradeTransaction");
        var agreement = trade?.Element(ram + "ApplicableSupplyChainTradeAgreement");
        var settlement = trade?.Element(ram + "ApplicableSupplyChainTradeSettlement");

        var seller = agreement?.Element(ram + "SellerTradeParty");
        var buyer = agreement?.Element(ram + "BuyerTradeParty");
        var summation = settlement?.Element(ram + "SpecifiedTradeSettlementMonetarySummation");
        var payment = settlement?.Element(ram + "SpecifiedTradeSettlementPaymentMeans");

        var sellerTaxId = seller?
            .Elements(ram + "SpecifiedTaxRegistration")
            .FirstOrDefault(e => (string?)e.Element(ram + "ID")?.Attribute("schemeID") == "VA")
            ?.Element(ram + "ID")?.Value ?? string.Empty;

        return new ZugferdInvoice
        {
            InvoiceNumber = header?.Element(ram + "ID")?.Value ?? string.Empty,
            InvoiceDate = ParseDate(header?.Element(ram + "IssueDateTime")
                                             ?.Element(udt + "DateTimeString")?.Value),
            SellerName = seller?.Element(ram + "Name")?.Value ?? string.Empty,
            SellerTaxId = sellerTaxId,
            BuyerName = buyer?.Element(ram + "Name")?.Value ?? string.Empty,
            CurrencyCode = settlement?.Element(ram + "InvoiceCurrencyCode")?.Value ?? "EUR",
            TotalAmount = ParseDecimal(summation?.Element(ram + "GrandTotalAmount")?.Value),
            TaxAmount = ParseDecimal(summation?.Element(ram + "TaxTotalAmount")?.Value),
            Iban = payment?.Element(ram + "PayeePartyCreditorFinancialAccount")
                                     ?.Element(ram + "IBANID")?.Value ?? string.Empty,
            RawXml = xml,
            ImportedAt = DateTime.UtcNow
        };
    }

    // ── Hilfsmethoden ────────────────────────────────────────────────────────
    private static DateTime ParseDate(string? value)
    {
        if (string.IsNullOrWhiteSpace(value)) return DateTime.MinValue;
        return DateTime.TryParseExact(value.Trim(), "yyyyMMdd",
            System.Globalization.CultureInfo.InvariantCulture,
            System.Globalization.DateTimeStyles.None, out var dt) ? dt : DateTime.MinValue;
    }

    private static decimal ParseDecimal(string? value)
        => decimal.TryParse(value,
            System.Globalization.NumberStyles.Any,
            System.Globalization.CultureInfo.InvariantCulture, out var d) ? d : 0m;
}