/*
 This file is part of GNU Taler
 (C) 2024 Taler Systems S.A.

 GNU Taler is free software; you can redistribute it and/or modify it under the
 terms of the GNU General Public License as published by the Free Software
 Foundation; either version 3, or (at your option) any later version.

 GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

 You should have received a copy of the GNU General Public License along with
 GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */

/**
 * Imports.
 */
import { Amounts } from "./amounts.js";
import { parsePaytoUri } from "./payto.js";

type EncodeResult = { type: "ok"; qrContent: string } | { type: "skip" };

/**
 * See "Schweizer Implementation Guidelines QR-Rechnung".
 */
function encodePaytoAsSwissQrBill(paytoUri: string): EncodeResult {
  const parsedPayto = parsePaytoUri(paytoUri);
  if (!parsedPayto) {
    throw Error("invalid payto URI");
  }
  if (parsedPayto.targetType !== "iban") {
    return { type: "skip" };
  }
  const amountStr = parsedPayto.params["amount"];
  if (amountStr === undefined) {
    return { type: "skip" };
  }
  const targetPathParts = parsedPayto.targetPath.split("/");
  const beneficiaryIban = targetPathParts[targetPathParts.length - 1];
  const countryCode = beneficiaryIban.slice(0, 2).toUpperCase();
  if (countryCode !== "CH") {
    // We only show the Swiss payment QR code for Swiss IBANs
    return { type: "skip" };
  }
  const lines = [
    "SPC", // QRType
    "0200", // Version
    "1", // Character set (1: UTF-8)
    beneficiaryIban, // Beneficiary IBAN
    // Group: Beneficiary
    "S", // Address type (S: structured)
    parsedPayto.params["receiver-name"], // Beneficiary name
    "", // street
    "", // apt. nr.
    parsedPayto.params["receiver-postal-code"], // town, // postal code
    parsedPayto.params["receiver-town"], // town
    countryCode, // Country
    // Group: Ultimate Debtor (not used in version 0200)
    "", // Ultimate Debtor Address type (S: structured)
    "", // Ultimate Debtor name
    "", // Ultimate Debtor street
    "", // Ultimate Debtor apt. nr
    "", // Ultimate Debtor postal code
    "", // Ultimate Debtor town
    "", // Ultimate Debtor country
    // Group: Amount
    Amounts.stringifyValue(amountStr, 2), // Amount
    Amounts.currencyOf(amountStr), // Currency
    // Group: Debtor
    "", // Address type (S: structured)
    "", // Debtor name
    "", // Debtor street
    "", // Debtor apt. nr
    "", // Debtor postal code
    "", // Debtor town
    "", // Debtor country
    // Group: Reference
    "NON", // reference type
    "", // Reference
    // Group: Additional Information
    parsedPayto.params["message"], // Unstructured data
    "EPD", // End of payment data
  ];

  return {
    type: "ok",
    qrContent: lines.join("\n"),
  };
}

/**
 * See "Quick Response Code - Guidelines to
 * Enable the Data Capture for the
 * Initiation of a SEPA Credit Transfer".
 */
function encodePaytoAsEpcQr(paytoUri: string): EncodeResult {
  const parsedPayto = parsePaytoUri(paytoUri);
  if (!parsedPayto) {
    throw Error("invalid payto URI");
  }
  if (parsedPayto.targetType !== "iban") {
    return { type: "skip" };
  }
  const amountStr = parsedPayto.params["amount"];
  const targetPathParts = parsedPayto.targetPath.split("/");
  const beneficiaryIban = targetPathParts[targetPathParts.length - 1];
  const countryCode = beneficiaryIban.slice(0, 2).toUpperCase();
  switch (countryCode) {
    case "CH":
      // Switzerland has its own QR code, do not generate EPC.
      return { type: "skip" };
  }
  const amt =
    amountStr !== undefined
      ? `${Amounts.currencyOf(amountStr)}${Amounts.stringifyValue(
          amountStr,
          2,
        )}`
      : "";
  const lines = [
    "BCD", // service tag
    "002", // version
    "1", // character set (1: UTF-8)
    "SCT", // Identification
    "", // optional BIC
    parsedPayto.params["receiver-name"], // Beneficiary name
    beneficiaryIban, // Beneficiary IBAN
    amt, // Amount (optional)
    "", // AT-44 Purpose
    parsedPayto.params["message"], // AT-05 Unstructured remittance information
  ];
  return {
    type: "ok",
    qrContent: lines.join("\n"),
  };
}

/**
 * Specification of a QR code that includes payment information.
 */
export interface QrCodeSpec {
  /**
   * Type of the QR code.
   *
   * Depending on the type, different visual styles
   * might be applied.
   */
  type: string;

  /**
   * Content of the QR code that should be rendered.
   */
  qrContent: string;
}

/**
 * Get applicable QR code specifications for the given payto URI.
 */
export function getQrCodesForPayto(paytoUri: string): QrCodeSpec[] {
  const res: QrCodeSpec[] = [];
  {
    const qr = encodePaytoAsEpcQr(paytoUri);
    if (qr.type == "ok") {
      res.push({
        type: "epc-qr",
        qrContent: qr.qrContent,
      });
    }
  }
  {
    const qr = encodePaytoAsSwissQrBill(paytoUri);
    if (qr.type == "ok") {
      res.push({
        type: "spc",
        qrContent: qr.qrContent,
      });
    }
  }
  return res;
}
