using KarleyLibrary.Erweiterungen;
using s2industries.ZUGFeRD;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WK5.Core.Drucken;
using WK5.Core.Drucken.Aufträge;
using WK5.Core.Drucken.Angebote;
using WK5.Core.Drucken.Lieferscheine;
using WK5.Core.Drucken.Rechnungen;
using WK5.Core.Drucken.Gutschriften;
using WK5.Core.Models;
using System.IO;
using System.Web;
using Serilog;
using Serilog.Sinks.Firebird;

namespace WK5.Core.Services
{

    public enum SteuerGründe
    {
        Drittland,
        GültigeUstID,
        WirdBerechnet
    }

    public static class XRechnungDefinitions
    {
        public const string Version200 = "urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_2.0";
        public const string Version211 = "urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_2.1";
        public const string Version220 = "urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_2.2";
        public const string Version120 = "urn:cen.eu:en16931:2017#compliant#urn:xoev-de:kosit:standard:xrechnung_1.2";
    }

    public class XRechnungService
    {

        public async Task<InvoiceDescriptor> ToXRechnung(Beleg beleg)
        {
            using FbController2 fbController = new FbController2();

            TaxCategoryCodes defaultTaxCategory = TaxCategoryCodes.S;
            TaxCategoryCodes exemptTaxCategory = TaxCategoryCodes.E;

            TaxExemptionReasonCodes? exemptionReasonCode = null;
            string? exemptionReason = null;


            SteuerGründe steuerGrund = await GetSteuerGrund(beleg, await Land.GetLänderAsync(fbController).ToListAsync());
            switch (steuerGrund)
            {
                case SteuerGründe.Drittland:
                    exemptTaxCategory = TaxCategoryCodes.G;
                    exemptionReasonCode = TaxExemptionReasonCodes.vatex_eu_132_1g;
                    exemptionReason = "Lieferung an Drittland außerhalb der EU";
                    break;
                case SteuerGründe.GültigeUstID:
                    exemptTaxCategory = TaxCategoryCodes.K;
                    exemptionReason = "Lieferung an EU Land mit gültiger UstId";
                    break;
                default:
                    break;
            }



            OptionCollection optionen = await Option.GetOptionenAsync(fbController);
            Mehrwertsteuer? versandMwst = await Mehrwertsteuer.GetVersandMehrwertsteuerAsync(fbController);
            Kunde? kunde = await Kunde.GetKundeAsync(beleg.Kundennummer);
            if (kunde is null)
            {
                throw new Exception("Der Kunde wurde nicht gefunden!");
            }

            InvoiceDescriptor desc = InvoiceDescriptor.CreateInvoice($"{beleg.Belegtyp}-{beleg.Belegnummer}", beleg.BELE_D_DATE, CurrencyCodes.EUR);
#if DEBUG
            desc.ReferenceOrderNo = String.IsNullOrWhiteSpace(kunde.KUND_WK5_A_LEITWEG_ID) ? "DEBUG" : kunde.KUND_WK5_A_LEITWEG_ID;
#else
            desc.ReferenceOrderNo = kunde.KUND_WK5_A_LEITWEG_ID;
#endif
            desc.ActualDeliveryDate = beleg.Liefertermin;
            desc.OrderNo = beleg.Bestellnummer;

            #region Verkäufer Daten            
            FirmenDaten firma = GlobalConfig.Configuration.FirmenDaten;
            desc.SetSeller(
                name: firma.Firmenname,
                postcode: firma.Firmenanschrift.Postleitzahl,
                city: firma.Firmenanschrift.Ort,
                street: firma.Firmenanschrift.Strasse,
                country: GetCountry(firma.Firmenanschrift.Land),
                id: ""
            );


            desc.SetSellerContact(
                name: firma.Firmenname,
                orgunit: "Vertrieb",
                emailAddress: firma.Email,
                phoneno: firma.Telefon,
                faxno: firma.Fax
            );


            desc.AddSellerTaxRegistration(no: firma.UstId, schemeID: TaxRegistrationSchemeID.VA);
            desc.AddSellerTaxRegistration(no: firma.Steuernummer, schemeID: TaxRegistrationSchemeID.FC);
            desc.ShipFrom = desc.Seller;
            #endregion

            #region Käufer Daten            
            string kundenName = String.Join(' ', kunde.KUND_A_NAME1, kunde.KUND_A_NAME2, kunde.KUND_A_NAME3).Trim();
            desc.SetBuyer(
                name: kundenName,
                postcode: beleg.Postleitzahl,
                city: beleg.Ort,
                street: beleg.Strasse,
                country: GetCountry(kunde.KUND_A_LAND),
                id: beleg.Kundennummer
            );

            Ansprechpartner? partner = await Ansprechpartner.GetAnsprechpartnerByNameAsync(beleg.Kundennummer, beleg.Ansprechpartner);
            if (partner is not null)
            {
                desc.SetBuyerContact(
                    name: $"{partner.PART_A_VORNAME} {partner.PART_A_NAME}",
                    orgunit: partner.PART_A_BEREICH ?? String.Empty,
                    emailAddress: partner.PART_A_EMAIL,
                    phoneno: partner.PART_A_TELEFON ?? kunde.KUND_A_TEL1 ?? kunde.KUND_A_TEL2 ?? String.Empty,
                    faxno: partner.PART_A_TELEFAX ?? kunde.KUND_A_FAX ?? String.Empty
                );
            }
            else
            {
                desc.SetBuyerContact(
                    name: kundenName,
                    orgunit: String.Empty,
                    emailAddress: kunde.KUND_A_EMAIL ?? String.Empty,
                    phoneno: kunde.KUND_A_TEL1 ?? kunde.KUND_A_TEL2 ?? String.Empty,
                    faxno: kunde.KUND_A_FAX ?? String.Empty
                );
            }

            if (desc.Buyer.Country == CountryCodes.DE)
            {
                if (!String.IsNullOrWhiteSpace(kunde.KUND_A_STEUERNR))
                {
                    desc.AddBuyerTaxRegistration(kunde.KUND_A_STEUERNR, TaxRegistrationSchemeID.FC);
                }
            }

            if (!String.IsNullOrWhiteSpace(kunde.KUND_A_USTIDNR))
            {
                desc.AddBuyerTaxRegistration(kunde.KUND_A_USTIDNR, TaxRegistrationSchemeID.VA);
            }
            #endregion

            #region Lieferadresse            
            desc.ShipTo = desc.Buyer;

            if (beleg.LieferanschriftId > 0)
            {
                Lieferanschrift? lieferanschrift = await Lieferanschrift.GetLieferanschriftAsync(beleg.Kundennummer, beleg.LieferanschriftId);
                if (lieferanschrift is not null)
                {
                    desc.ShipTo = new Party()
                    {
                        City = lieferanschrift.KULA_A_ORT,
                        Postcode = lieferanschrift.KULA_A_PLZ,
                        Street = lieferanschrift.KULA_A_STRASSE,
                        Name = String.Join(' ', lieferanschrift.KULA_A_NAME1, lieferanschrift.KULA_A_NAME2, lieferanschrift.KULA_A_NAME3).Trim(),
                        ContactName = lieferanschrift.KULA_A_PARTNER ?? String.Empty,
                        Country = GetCountry(lieferanschrift.KULA_A_LAND)

                    };
                }
            }
            #endregion

            #region Positionen

            // Wir müssen das individuelle Netto Total unserer Lines tracken
            decimal lineTotal = 0.0m;

            // Wir müssen tracken wieviel Rabatt wir geben
            decimal lineAllowance = 0.0m;

            // Müssen wegen Rundungen individuell getrackt werden und dürfen nicht über den Beleg bezogen werden
            decimal lineMwst = 0.0m;

            //Wir müssen unsere verschiedenen Steuersätze mit Summe tracken
            Dictionary<decimal, decimal> tradeTaxes = new Dictionary<decimal, decimal>();

            foreach (Belegposition pos in beleg.Positionen)
            {
                string sanitizedLangtext = HttpUtility.HtmlDecode(pos.Langtext.RemoveHTML());

                if (pos.Artikelnummer == "TEXT")
                {
                    TradeLineItem lineItem = desc.AddTradeLineItem(
                        lineID: pos.PosNr.ToString(),
                        name: "Hinweis-Text",
                        description: null,
                        unitCode: QuantityCodes.C62,
                        billedQuantity: pos.Menge,
                        taxType: TaxTypes.VAT,
                        comment: sanitizedLangtext,
                        sellerAssignedID: pos.Artikelnummer
                    );
                }
                else
                {
                    Kundenrabatt? sachnummer = await Kundenrabatt.GetKundenSachnummerAsync(beleg.Kundennummer, pos.Artikelnummer);
                    string kundenArtikelnummer = String.Empty;
                    if (sachnummer is not null && !String.IsNullOrWhiteSpace(sachnummer.KUSA_A_ARTNUMMKUND))
                    {
                        kundenArtikelnummer = sachnummer.KUSA_A_ARTNUMMKUND;
                    }

                    string posName = String.Join(' ', pos.Bezeichnung1, pos.Bezeichnung2, pos.Bezeichnung3, pos.Bezeichnung4, pos.Bezeichnung5).Trim();

                    GlobalID? globalID = null;
                    if (!String.IsNullOrWhiteSpace(pos.ARTI_A_EAN))
                    {
                        globalID = new GlobalID(GlobalID.SchemeID_EAN, pos.ARTI_A_EAN);
                    }

                    decimal roundedPreisMitRabatt = Math.Round(pos.PreisMitRabatt, 2, MidpointRounding.AwayFromZero);
                    decimal roundedPreis = Math.Round(pos.Preis, 2, MidpointRounding.ToZero);
                    decimal itemValue = roundedPreisMitRabatt * pos.Menge;

                    TradeLineItem lineItem = desc.AddTradeLineItem(
                        lineID: pos.PosNr.ToString(),
                        name: posName,
                        description: pos.Seriennummern.Count > 0 ? $"SN: {String.Join(", ", pos.Seriennummern.Select(x => x.Nummer).ToArray()).Trim()}" : null,
                        unitCode: QuantityCodes.C62,
                        unitQuantity: null,
                        billedQuantity: pos.Menge,
                        taxType: TaxTypes.VAT,
                        comment: pos.LangtextDrucken ? sanitizedLangtext : String.Empty,
                        id: globalID,
                        sellerAssignedID: pos.Artikelnummer,
                        buyerAssignedID: kundenArtikelnummer,
                        deliveryNoteID: pos.PosNr.ToString(),
                        deliveryNoteDate: null,
                        buyerOrderID: "",
                        buyerOrderDate: null,
                        billingPeriodStart: null,
                        billingPeriodEnd: null
                    );


                    // Reine Rabatte dürfen nur als Allowance abgebildet werden
                    if (pos.PreisMitRabatt >= 0)
                    {
                        lineItem.NetUnitPrice = roundedPreisMitRabatt;
                        lineItem.GrossUnitPrice = roundedPreis;

                        lineItem.AddTradeAllowanceCharge(true, CurrencyCodes.EUR, roundedPreis, 0, String.Empty);
                        lineTotal += itemValue;
                    }
                    else
                    {
                        lineItem.NetUnitPrice = 0;
                        lineItem.GrossUnitPrice = 0;

                        decimal allowance = Math.Abs(itemValue);

                        lineItem.AddTradeAllowanceCharge(true, CurrencyCodes.EUR, beleg.GetNetto(optionen), allowance, String.Empty);
                        lineAllowance += allowance;

                        desc.AddTradeAllowanceCharge(true, beleg.GetNetto(optionen), CurrencyCodes.EUR, allowance, posName, TaxTypes.VAT, pos.MwstSatz > 0 ? defaultTaxCategory : exemptTaxCategory, Math.Max(0, pos.MwstSatz));
                    }

                    if (beleg.MwstBerechnen)
                    {
                        lineItem.TaxCategoryCode = defaultTaxCategory;
                        lineItem.TaxPercent = pos.MwstSatz;
                        lineMwst += itemValue * pos.MwstSatz / 100;
                    }
                    else
                    {
                        lineItem.TaxCategoryCode = exemptTaxCategory;
                        lineItem.TaxPercent = 0;
                    }

                    // Taxes tracken
                    if (!tradeTaxes.ContainsKey(lineItem.TaxPercent))
                    {
                        tradeTaxes.Add(lineItem.TaxPercent, 0.0m);
                    }

                    tradeTaxes[lineItem.TaxPercent] += itemValue;
                }
            }

            decimal versandTaxProzent;
            TaxCategoryCodes versandTaxCategory;
            if (beleg.MwstBerechnen)
            {
                versandTaxProzent = versandMwst!.MWST_N_PROZENT; // Aussage Klein: Wenn MwstBerechnen = true ist muss es eine versandMWST geben
                versandTaxCategory = defaultTaxCategory;
                lineMwst += beleg.Zusatzkosten * versandTaxProzent / 100;
            }
            else
            {
                versandTaxProzent = 0;
                versandTaxCategory = exemptTaxCategory;
            }

            if (beleg.Frachtkosten > 0)
            {
                desc.AddTradeAllowanceCharge(false, 0.0m, CurrencyCodes.EUR, beleg.Frachtkosten, "Frachtkosten", TaxTypes.VAT, versandTaxCategory, versandTaxProzent);
            }

            if (beleg.Versicherungskosten > 0)
            {
                desc.AddTradeAllowanceCharge(false, 0.0m, CurrencyCodes.EUR, beleg.Versicherungskosten, "Versicherungskosten", TaxTypes.VAT, versandTaxCategory, versandTaxProzent);
            }

            if (beleg.Verpackungskosten > 0)
            {
                desc.AddTradeAllowanceCharge(false, 0.0m, CurrencyCodes.EUR, beleg.Verpackungskosten, "Verpackungskosten", TaxTypes.VAT, versandTaxCategory, versandTaxProzent);
            }

            #endregion

            #region Steuern

            //Steuern für Zusatzkosten hinzufügen            
            List<KeyValuePair<decimal, decimal>> chargeTaxes = new List<KeyValuePair<decimal, decimal>>
            {
                new KeyValuePair<decimal,decimal>(versandTaxProzent, beleg.Frachtkosten ),
                new KeyValuePair<decimal,decimal>(versandTaxProzent, beleg.Versicherungskosten ),
                new KeyValuePair<decimal,decimal>(versandTaxProzent, beleg.Verpackungskosten )
            };

            foreach (KeyValuePair<decimal, decimal> kvp in chargeTaxes)
            {
                if (!tradeTaxes.ContainsKey(kvp.Key))
                {
                    tradeTaxes.Add(kvp.Key, 0.0m);
                }

                tradeTaxes[kvp.Key] += kvp.Value;
            }


            foreach (KeyValuePair<decimal, decimal> tax in tradeTaxes)
            {
                if (beleg.MwstBerechnen)
                {
                    desc.AddApplicableTradeTax(
                        basisAmount: tax.Value,
                        percent: tax.Key,
                        typeCode: TaxTypes.VAT,
                        categoryCode: defaultTaxCategory,
                        allowanceChargeBasisAmount: 0,
                        exemptionReasonCode: null,
                        exemptionReason: null
                    );
                }
                else
                {
                    desc.AddApplicableTradeTax(
                           basisAmount: tax.Value,
                           percent: tax.Key,
                           typeCode: TaxTypes.VAT,
                           categoryCode: exemptTaxCategory,
                           allowanceChargeBasisAmount: 0,
                           exemptionReasonCode: exemptionReasonCode,
                           exemptionReason: exemptionReason
                       );
                }
            }

            #endregion

            #region Summen                        

            decimal mwst = lineMwst;
            decimal brutto = beleg.GetBruttoBetrag(optionen, versandMwst);

            decimal roundedMwst = Math.Round(mwst, 2, MidpointRounding.AwayFromZero);
            decimal roundedBrutto = Math.Round(brutto, 2, MidpointRounding.AwayFromZero);

            decimal netto = lineTotal + beleg.Zusatzkosten - lineAllowance;

            decimal rounding = roundedBrutto - (netto + roundedMwst);

#if DEBUG
            if (rounding > 0.2m)
            {
                throw new Exception("Rounding Wert fällt außerhalb der erwarteten Ergebnisse");
            }
#endif

            desc.SetTotals(
                lineTotalAmount: lineTotal,
                chargeTotalAmount: beleg.Zusatzkosten,
                allowanceTotalAmount: lineAllowance,
                taxBasisAmount: netto,
                taxTotalAmount: roundedMwst,
                grandTotalAmount: netto + roundedMwst,
                totalPrepaidAmount: beleg.WK5_BELE_N_BEZAHLT_WERT,
                duePayableAmount: roundedBrutto - beleg.WK5_BELE_N_BEZAHLT_WERT,
                roundingAmount: rounding
            );
            #endregion

            #region Zahlungsbedingungen
            ZahlungsbedingungCollection zahlungsbedingungen = await Zahlungsbedingung.GetZahlungsbedingungenAsync(fbController);
            Zahlungsbedingung zabd = beleg.GetZahlungsbedingung(zahlungsbedingungen);

            // Lastschrift
            if (zabd.ZABD_N_NR == 20)
            {
                string gläubigerIdentifikation = await GlobalConfig.GetConfigAsync(GlobalConfig.GLAEUBIGER_IDENTIFIKATIONSNUMMER_CONFIG_NAME, fbController);
                desc.SetPaymentMeansSepaDirectDebit(
                    sepaCreditorIdentifier: gläubigerIdentifikation,
                    sepaMandateReference: kunde.KUND_WK5_A_MANDATSREFERENZ,
                    information: zabd.ZABD_A_TEXT1
                );

                desc.AddDebitorFinancialAccount(
                    iban: kunde.KUND_A_IBAN,
                    bic: kunde.KUND_A_BIC,
                    id: kunde.KUND_A_BANKKONTO,
                    bankleitzahl: null,
                    bankName: kunde.KUND_A_BANKNAME
                );
            }
            else
            {
                desc.AddCreditorFinancialAccount(
                    iban: firma.IBAN,
                    bic: firma.BIC,
                    id: null,
                    bankleitzahl: null,
                    bankName: firma.Bankname,
                    name: firma.Firmenname
                );

                (PaymentMeansTypeCodes code, string zahlungsmittel) paymentMeans = ZabdToXRechnung(zabd.ZABD_N_NR);
                desc.SetPaymentMeans(
                    paymentCode: paymentMeans.code,
                    information: paymentMeans.zahlungsmittel
                );
            }

            desc.SetTradePaymentTerms(
                description: zabd.ZABD_A_TEXT1,
                dueDate: beleg.BELE_D_DATE.AddDays(zabd.ZABD_N_NETTOTAGE)
            );

            desc.PaymentReference = $"{beleg.Belegtyp}-{beleg.Belegnummer} Kunden-Nr.: {beleg.Kundennummer}";
            #endregion

            byte[]? pdfData = await GetBelegPDF(beleg, fbController);
            if (pdfData is not null)
            {
                desc.AddAdditionalReferencedDocument(
                    id: $"{beleg.Belegtyp}-{beleg.Belegnummer}",
                    typeCode: AdditionalReferencedDocumentTypeCode.ReferenceDocument,
                    name: $"{beleg.Belegtyp}-{beleg.Belegnummer} als PDF",
                    attachmentBinaryObject: pdfData,
                    filename: $"{beleg.Belegtyp}_{beleg.Belegnummer}.pdf"
                );
            }

            return desc;
        }

        private static async Task<byte[]?> GetBelegPDF(Beleg beleg, FbController2 fbController)
        {
            switch (EnumHelper.GetBelegTyp(beleg.Belegtyp))
            {
                case BelegTyp.Rechnung:
                    {
                        Rechnung? bele = await Rechnung.GetRechnungAsync(beleg.Belegnummer, fbController);
                        if (bele is not null)
                        {
                            PrintRechnungRegelsatz printRegeln = new PrintRechnungRegelsatz
                            {
                                ShowFooter = true,
                                ShowHeader = true,
                                IsHybridMail = false
                            };
                            PrintRechnung printer = await PrintRechnung.CreateAsync(bele, printRegeln, fbController);
                            return printer.GetBytes();
                        }
                    }
                    break;
                case BelegTyp.Auftrag:
                    {
                        Auftrag? bele = await Auftrag.GetAuftragAsync(beleg.Belegnummer, fbController);
                        if (bele is not null)
                        {
                            PrintAuftragRegelsatz printRegeln = new PrintAuftragRegelsatz
                            {
                                ShowFooter = true,
                                ShowHeader = true
                            };
                            PrintAuftrag printer = await PrintAuftrag.CreateAsync(bele, printRegeln, fbController);
                            return printer.GetBytes();
                        }
                    }
                    break;
                case BelegTyp.Lieferschein:
                    {
                        Lieferschein? bele = await Lieferschein.GetLieferscheinAsync(beleg.Belegnummer, fbController);
                        if (bele is not null)
                        {
                            PrintLieferscheinRegelsatz printRegeln = new PrintLieferscheinRegelsatz
                            {
                                ShowFooter = true,
                                ShowHeader = true
                            };
                            PrintLieferschein printer = await PrintLieferschein.CreateAsync(bele, printRegeln);
                            return printer.GetBytes();
                        }
                    }
                    break;
                case BelegTyp.Gutschrift:
                    {
                        Gutschrift? bele = await Gutschrift.GetGutschriftAsync(beleg.Belegnummer, fbController);
                        if (bele is not null)
                        {
                            PrintGutschriftRegelsatz printRegeln = new PrintGutschriftRegelsatz
                            {
                                ShowFooter = true,
                                ShowHeader = true
                            };
                            PrintGutschrift printer = await PrintGutschrift.CreateAsync(bele, printRegeln, fbController);
                            return printer.GetBytes();
                        }
                    }
                    break;
                case BelegTyp.Angebot:
                    {
                        Angebot? bele = await Angebot.GetAngebotAsync(beleg.Belegnummer, fbController);
                        if (bele is not null)
                        {
                            PrintAngebotRegelsatz printRegeln = new PrintAngebotRegelsatz
                            {
                                ShowFooter = true,
                                ShowHeader = true
                            };
                            PrintAngebot printer = await PrintAngebot.CreateAsync(bele, printRegeln, fbController);
                            return printer.GetBytes();
                        }
                    }
                    break;
                default:
                    break;
            }

            return null;
        }

        private static (PaymentMeansTypeCodes code, string zahlungsmittel) ZabdToXRechnung(int zabd)
        {
            switch (zabd)
            {
                case 10:
                case 11:
                case 12:
                case 13:
                case 14:
                case 15:
                case 16:
                case 17:
                case 18:
                case 19:
                case 22:
                case 25:
                case 26:
                case 27:
                case 28:
                case 29:
                case 33:
                case 34:
                case 35:
                case 36:
                case 37:
                case 38:
                    return (PaymentMeansTypeCodes.SEPACreditTransfer, "Überweisung");
                case 20:
                    return (PaymentMeansTypeCodes.SEPADirectDebit, "Lastschrift");
                case 21:
                case 23:
                    return (PaymentMeansTypeCodes.InCash, "Barzahlung");
                case 24:
                    return (PaymentMeansTypeCodes.AutomatedClearingHouseDebit, "Amazon");
                case 30:
                    return (PaymentMeansTypeCodes.AutomatedClearingHouseDebit, "PayPal");
                case 31:
                    return (PaymentMeansTypeCodes.AutomatedClearingHouseDebit, "Sofortüberweisung");
                case 32:
                    return (PaymentMeansTypeCodes.AutomatedClearingHouseDebit, "Billpay");
                default:
                    return (PaymentMeansTypeCodes.Unknown, "Unbekannt");
            }
        }

        private static CountryCodes GetCountry(string country) => (CountryCodes)Enum.Parse(typeof(CountryCodes), country);


        public static async Task<SteuerGründe> GetSteuerGrund(Beleg beleg, List<Land> länder)
        {
            if (!beleg.MwstBerechnen)
            {

                if (länder.Any(x => x.LAND_A_ID == beleg.Land && !x.WK5_LAND_L_ISTEULAND))
                {
                    return SteuerGründe.Drittland;
                }

                Kunde? kunde = await Kunde.GetKundeAsync(beleg.Kundennummer);
                if (kunde is not null)
                {
                    if (länder.Any(x => x.LAND_A_ID == beleg.Land && beleg.Land != "DE" && x.WK5_LAND_L_ISTEULAND && kunde.IstUstIdGültig))
                    {
                        return SteuerGründe.GültigeUstID;
                    }
                }
            }
            return SteuerGründe.WirdBerechnet;
        }

        public string ToPDF(InvoiceDescriptor xRechnung, ZUGFeRDVersion version, Profile profile, XRechnungVersion? xRechnungVersion = null)
        {
            using MemoryStream mem = new MemoryStream();


            xRechnung.Save(mem, version, profile);            

            string xml = Encoding.UTF8.GetString(mem.ToArray());

            if (profile == Profile.XRechnung && xRechnungVersion is not null)
            {
                string from = XRechnungDefinitions.Version211;
                string to = xRechnungVersion switch
                {
                    XRechnungVersion.Version200 => XRechnungDefinitions.Version200,
                    XRechnungVersion.Version211 => XRechnungDefinitions.Version211,
                    XRechnungVersion.Version220 => XRechnungDefinitions.Version220,
                    _ => XRechnungDefinitions.Version211
                };

                xml = xml.Replace(from, to);
            }
            else if (profile == Profile.XRechnung1)
            {
                xml = xml.Replace(XRechnungDefinitions.Version211, XRechnungDefinitions.Version120);
            }

            return xml;
        }

    }

    public enum XRechnungVersion
    {
        Version200 = 1,
        Version211 = 2,
        Version220 = 4
    }
}
