import { Component, FlowComponent, JSX, Show } from "solid-js";
import Address, { AddressData } from "./Address";
import SwissQrCode from "./SwissQrCode";

export type InvoiceData = {
  iban: string;
  amount: number;
  amountBeforeTax: number;
  tax: number;
  currency: string;
  message?: string;
  reference?: string;
  referenceType?: "QRR" | "SCOR" | "NON";
  creditor: AddressData;
  debtor?: AddressData;
};

export const formatAmount = (amount: number) => {
  return amount
    .toLocaleString("de-CH", {
      minimumFractionDigits: 2,
      maximumFractionDigits: 2,
    })
    .replaceAll("’", " ");
};

const spaceEveryX = (text: string, x = 4, reverse = false) => {
  text = text.replaceAll(" ", "");

  let result: string[] = [];

  let chars = text.split("");

  if (reverse) {
    chars = chars.reverse();
  }

  chars.forEach(function (v, i) {
    if (i % x === 0) {
      result.push(" ");
    }
    result.push(v);
  });

  if (reverse) {
    result = result.reverse();
  }

  return result.join("");
};

const encodeSwissQrInvoice = (
  invoiceData: InvoiceData,
  { type = "SPC" } = {}
) => {
  // This code implements version 2.2! But the guidelines require that this stays fixed as 2.0 ;)
  const VERSION = "0200";
  const CODING = 1; // utf-8
  const LINE_BREAK = "\n";
  const END_INDICATOR = "EPD";

  const creditorType = invoiceData.creditor.type || "S";
  const debtorType = invoiceData.debtor?.type || "S";

  const header: string[] = [type, VERSION, CODING.toString()];
  const creditor: string[] = [
    invoiceData.iban.replaceAll(" ", ""),
    creditorType,
    invoiceData.creditor.name,
    invoiceData.creditor.line1 || "",
    invoiceData.creditor.line2 || "",
    invoiceData.creditor.type === "S"
      ? (invoiceData.creditor.zip || 0).toString()
      : "",
    creditorType ? invoiceData.creditor.city || "" : "",
    invoiceData.creditor.country,
  ];
  const ultimateCreditor: string[] = [
    "", // type
    "", // name
    "", // line 1
    "", // line 2
    "", // zip
    "", // city
    "", // country
  ];
  const amount: string[] = [
    invoiceData.amount.toFixed(2),
    invoiceData.currency,
  ];
  const debtor: string[] = [
    debtorType ? invoiceData.debtor?.type || "" : "",
    invoiceData.debtor?.name || "",
    invoiceData.debtor?.line1 || "",
    invoiceData.debtor?.line2 || "",
    (invoiceData.debtor?.zip || "").toString(),
    invoiceData.debtor?.city || "",
    invoiceData.debtor?.country || "",
  ];
  const referenceType = !invoiceData.reference
    ? "NON"
    : invoiceData.referenceType ||
      (invoiceData.reference.startsWith("RF") ? "SCOR" : "QRR");
  const reference: string[] = [
    referenceType,
    (invoiceData.reference || "").replaceAll(" ", ""),
  ];

  const invoice = [
    header,
    creditor,
    ultimateCreditor,
    amount,
    debtor,
    reference,
    invoiceData.message || "",
    END_INDICATOR,
  ]
    .flat()
    .join(LINE_BREAK);

  return invoice;
};

const SwissInvoice: Component<
  { value: InvoiceData } & JSX.HTMLAttributes<HTMLElement>
> = (props) => {
  const HeaderEz: FlowComponent = (props) => (
    <div class="text-lg leading-normal font-bold mb-3">{props.children}</div>
  );
  const HeaderZ: FlowComponent = (props) => (
    <div class="text-sm leading-normal font-bold">{props.children}</div>
  );
  const HeaderE: FlowComponent = (props) => (
    <div class="text-xs leading-normal font-bold">{props.children}</div>
  );

  // Important: pt-px was added because: break-inside-avoid with the top-border results in a wrong pagebreak in chrome,
  // the top-border remained on the first page and only the rest of the invoice was on the second page.
  // AND: this only happend if scroll position was at the bottom.
  // FIXME: Chrome should fix the break-inside-avoid calculation, when chrome has fixed their issue the pt-px can be removed.

  return (
    <div class="break-inside-avoid pt-px">
      <div class="swissinvoice whitespace-normal break-words -max-w-[1200px] aspect-[2/1] print:w-full flex-shrink-0 text-left print:text-black text-sm bg-white border-y border-black border-dashed grid grid-flow-col grid-cols-[29.5%_26.5%_auto] grid-rows-[auto_auto_1fr]">
        <div class="col-span-1 row-span-1 p-10 pb-0 border-r border-black border-dashed">
          <HeaderEz>Empfangsschein</HeaderEz>
          <div class="leading-tight flex flex-col gap-4">
            <div>
              <HeaderE>Konto / Zahlbar an</HeaderE>
              <div>{spaceEveryX(props.value.iban)}</div>
              <Address address={props.value.creditor} />
            </div>
            <Show when={props.value.reference}>
              <div>
                <HeaderE>Referenz</HeaderE>
                <div>{spaceEveryX(props.value.reference!)}</div>
              </div>
            </Show>
            <Show when={props.value.debtor}>
              <div>
                <HeaderE>Zahlbar durch</HeaderE>
                <Address address={props.value.debtor!} />
              </div>
            </Show>
          </div>
        </div>
        <div class="col-span-1 row-span-2 p-10 pt-10 border-r border-black border-dashed">
          <div class="grid grid-cols-2 gap-x-2">
            <HeaderE>Währung</HeaderE>
            <HeaderE>Betrag</HeaderE>
            <div>{props.value.currency}</div>
            <div>{formatAmount(props.value.amount)}</div>
          </div>
          <div class="text-right text-xs font-bold mt-10 leading-tight">
            Annahmestelle
          </div>
        </div>

        <div class="col-span-1 row-span-1 pt-10 pl-10">
          <HeaderEz>Zahlteil</HeaderEz>
          <SwissQrCode
            value={encodeSwissQrInvoice(props.value)}
            class="w-full aspect-square"
          />
        </div>
        <div class="col-span-1 row-span-1 pt-10 pl-10 pb-10">
          <div class="grid grid-cols-2 gap-x-2">
            <HeaderZ>Währung</HeaderZ>
            <HeaderZ>Betrag</HeaderZ>
            <div>{props.value.currency}</div>
            <div>{formatAmount(props.value.amount)}</div>
          </div>
        </div>

        <div class="col-span-2 pl-10"></div>

        <div class="overflow-hidden leading-tight text-base col-span-1 row-span-2 p-10 flex flex-col gap-4">
          <div>
            <HeaderZ>Konto / Zahlbar an</HeaderZ>
            <div>{spaceEveryX(props.value.iban)}</div>
            <Address address={props.value.creditor} />
          </div>
          <Show when={props.value.reference}>
            <div>
              <HeaderZ>Referenz</HeaderZ>
              <div>{spaceEveryX(props.value.reference!)}</div>
            </div>
          </Show>
          <Show when={props.value.message}>
            <div>
              <HeaderZ>Zusätzliche Informationen</HeaderZ>
              <div>{props.value.message}</div>
            </div>
          </Show>
          <Show when={props.value.debtor}>
            <div>
              <HeaderE>Zahlbar durch</HeaderE>
              <Address address={props.value.debtor!} />
            </div>
          </Show>
        </div>
      </div>
    </div>
  );
};

export default SwissInvoice;
