import { zugferd } from "node-zugferd";
import { BASIC } from "node-zugferd/profile/basic";
import type { CountryCode } from "node-zugferd/codelist/country";
import type { InvoiceDoc } from "@/models/Invoice";

// Falls im Client-/Settings-Datensatz mal ein ungültiger Ländercode landet,
// lieber sicher auf DE zurückfallen als die ganze Rechnung crashen zu lassen.
function toCountryCode(code: string): CountryCode {
  return (code || "DE") as CountryCode;
}

// BASIC ist laut node-zugferd-Doku das Mindestprofil, das in Deutschland
// überhaupt als elektronische Rechnung gilt (MINIMUM/BASIC-WL reichen
// rechtlich nicht aus) — siehe GoBD-Hinweis im Projekt-README.
const invoicer = zugferd({ profile: BASIC });

function fmt2(n: number): string {
  return n.toFixed(2);
}

function addDays(date: Date, days: number): Date {
  return new Date(date.getTime() + days * 24 * 60 * 60 * 1000);
}

export function buildZugferdData(invoice: InvoiceDoc): typeof invoicer.$Infer.Schema {
  const vatCategoryCode = invoice.kleinunternehmer ? "E" : "S";
  const vatRate = invoice.kleinunternehmer ? 0 : invoice.vatRate;

  return {
    number: invoice.number,
    typeCode: "380",
    issueDate: new Date(invoice.issueDate),
    transaction: {
      line: [
        {
          identifier: "1",
          tradeProduct: {
            name: invoice.description,
          },
          tradeAgreement: {
            netTradePrice: {
              chargeAmount: fmt2(invoice.netAmount),
            },
          },
          tradeDelivery: {
            billedQuantity: {
              unitMeasureCode: "C62",
              amount: "1.0000",
            },
          },
          tradeSettlement: {
            tradeTax: {
              typeCode: "VAT",
              categoryCode: vatCategoryCode,
              rateApplicablePercent: String(vatRate),
            },
            monetarySummation: {
              lineTotalAmount: fmt2(invoice.netAmount),
            },
          },
        },
      ],
      tradeAgreement: {
        seller: {
          // BT-27 verlangt den vollen rechtlichen Namen des Verkäufers — bei
          // Einzelunternehmen mit Markennamen ist das der bürgerliche Name des
          // Inhabers, nicht der Markenname allein (siehe § 14 UStG).
          name:
            invoice.businessLegalName && invoice.businessLegalName !== invoice.businessName
              ? `${invoice.businessLegalName} (${invoice.businessName})`
              : invoice.businessName,
          postalAddress: {
            countryCode: toCountryCode(invoice.businessCountryCode),
            postCode: invoice.businessPostCode,
            line1: invoice.businessStreet,
            city: invoice.businessCity,
          },
          taxRegistration: invoice.businessTaxId
            ? {
                localIdentifier: invoice.businessTaxId,
              }
            : undefined,
        },
        buyer: {
          name: invoice.clientName,
          postalAddress: {
            countryCode: toCountryCode(invoice.clientCountryCode),
            postCode: invoice.clientPostCode,
            line1: invoice.clientStreet,
            city: invoice.clientCity,
          },
        },
      },
      tradeDelivery: {
        information: {
          deliveryDate: new Date(invoice.issueDate),
        },
      },
      tradeSettlement: {
        currencyCode: "EUR",
        remittanceInformation: invoice.paymentReference,
        vatBreakdown: [
          {
            calculatedAmount: fmt2(invoice.vatAmount),
            typeCode: "VAT",
            basisAmount: fmt2(invoice.netAmount),
            categoryCode: vatCategoryCode,
            rateApplicablePercent: String(vatRate),
            exemptionReasonText: invoice.kleinunternehmer
              ? "Gemäß § 19 UStG wird keine Umsatzsteuer berechnet."
              : undefined,
          },
        ],
        paymentInstruction: invoice.businessIban
          ? {
              typeCode: "58",
              transfers: [
                {
                  paymentAccountIdentifier: invoice.businessIban.replace(/\s+/g, ""),
                },
              ],
            }
          : undefined,
        paymentTerms: {
          dueDate: addDays(new Date(invoice.issueDate), 14),
        },
        monetarySummation: {
          lineTotalAmount: fmt2(invoice.netAmount),
          chargeTotalAmount: "0.00",
          allowanceTotalAmount: "0.00",
          taxBasisTotalAmount: fmt2(invoice.netAmount),
          taxTotal: {
            amount: fmt2(invoice.vatAmount),
            currencyCode: "EUR",
          },
          grandTotalAmount: fmt2(invoice.totalAmount),
          duePayableAmount: fmt2(invoice.totalAmount),
        },
      },
    },
  };
}

export async function embedZugferd(basePdf: Uint8Array, invoice: InvoiceDoc): Promise<Uint8Array> {
  const data = buildZugferdData(invoice);
  const document = invoicer.create(data);
  return document.embedInPdf(basePdf, {
    metadata: {
      title: `Rechnung ${invoice.number}`,
      author: invoice.businessName,
    },
  });
}
