#!/usr/bin/env -S /usr/bin/node

require("dotenv").config({ quiet: true });
const decimal = require("decimal.js");
const randomUUID = require("crypto").randomUUID;
const DEBUG = true;

const AUTH_URL =
  "https://genial-arquitetura-authentication.homolog.api.genial.systems";
const COREBK_URL =
  "https://genial-arquitetura-corebank.homolog.api.genial.systems";
const BAAS_URL = "https://gerador-arquitetura-baas.homolog.api.genial.systems";
const OPS_URL = "https://genial-baas-operations.homolog.api.genial.systems";

async function main() {
  const args = process.argv.slice(2);
  if (args.length === 0) return help();
  const cmd = args[0];
  const rest = args.slice(1);

  try {
    switch (cmd) {
      case "token": {
        const [ok, tok] = await getToken();
        if (!ok) return fail(tok);
        console.log(tok);
        break;
      }
      case "balance": {
        await Balance(rest[0]);
        break;
      }
      case "getkey": {
        if (rest.length < 1) return fail("Usage: getkey <pixKey>");
        await getKey(rest[0]);
        break;
      }
      case "decode": {
        if (rest.length < 1) return fail("Usage: decodeQrcode <qrcode>");
        await decodeQrcode(rest[0]);
        break;
      }
      case "payQr": {
        if (rest.length < 2)
          return fail("Usage: payQr <qrCodeCopyPaste>  <value>");
        console.log(rest[0], rest[1]);
        await PayQr(rest[0], rest[1]);
        break;
      }
      case "qrcodeStatic": {
        await qrcodeStatic();
        break;
      }
      case "paykey": {
        if (rest.length < 2) return fail("Usage: payInit <pixKey> <value>");
        await PayInit(rest[0], rest[1]);
        break;
      }
      case "qrcodedynamic": {
        if (rest.length < 1) return fail("Usage: qrcode  <value>");
        await QrCodeDynamic(rest[0]);
        break;
      }
      case "statusIn": {
        //not working on my machine
        if (rest.length < 1) return fail("Usage: statusIn <transactionId>");
        await StatusIn(rest[0]);
        break;
      }
      case "statusOut": {
        //not working on my machine
        if (rest.length < 1) return fail("Usage: statusOut <e2e>");
        await StatusOut(rest[0]);
        break;
      }
      case "refund": {
        if (rest.length < 1) return fail("Usage: refund <eventId> <value>");
        await Refund(rest[0], rest[1]);
        break;
      }
      default:
        help();
        return;
    }
  } catch (e) {
    fail(e.message || e);
    help();
    return;
  }
}

function help() {
  console.log(`Usage: genial-cli <command> [args]
Commands:
  token
  balance <accountNumber>
  getkey <accountNumber> <pixKey>
  statusOut <instantPaymentId> <YYYY-MM-DD>
  statusIn <transactionId>
  qrcode <accountNumber> <value>
  payInit [value] [pixKey]
  pay <idempotencyId>
  payQr <accountNumber> <value> <qrCodeCopyPaste>
  refund <eventId> <value>
  preview <emv>
  boletoPay <taxId> <line> [description] [uuid]
  boletoGet <id>
`);
  process.exit(1);
}

function fail(msg) {
  console.error("Error:", msg);
  process.exit(1);
}

/* =============== Auth =============== */

async function getToken() {
  const url = `${AUTH_URL}/v1/authentication`;
  const options = {
    method: "POST",
    headers: {
      basic: process.env.TOKEN_BASIC,
      "X-ORIGEM": process.env.TOKEN_XORIGEM,
    },
  };
  try {
    if (DEBUG) {
      console.log("\nDEBUG callApi:", options.method, url);
    }
    const res = await fetch(url, options);
    const text = await res.text();
    if (DEBUG) {
      console.log("DEBUG getToken status:", res.status);
      console.log("DEBUG getToken body:", text);
    }
    if (res.status < 200 || res.status >= 300)
      return [false, { status: res.status, body: text }];
    const json = JSON.parse(text);
    if (!json.token) return [false, "No token in response"];
    return [true, json.token];
  } catch (e) {
    return [false, e.message];
  }
}

async function callApi(method, url, body) {
  const [ok, token] = await getToken();
  if (!ok) return [false, token];
  const headers = {
    Authorization: token,
    "X-ORIGEM": process.env.TOKEN_XORIGEM,
  };
  const options = { method, headers };

  if (method.toUpperCase() === "POST") {
    headers["Content-Type"] = "application/json";
    options.body = body ? JSON.stringify(body) : "";
  }

  if (DEBUG) {
    console.log("\nDEBUG callApi:", method, url);
    if (body) console.log("DEBUG body:", JSON.stringify(body));
  }

  try {
    const res = await fetch(url, options);
    const text = await res.text();
    if (DEBUG) {
      console.log("DEBUG status:", res.status);
      console.log("DEBUG raw:", text);
    }
    if (res.status < 200 || res.status >= 300)
      return [false, { status: res.status, body: text }];
    try {
      return [true, JSON.parse(text)];
    } catch {
      return [true, text];
    }
  } catch (e) {
    return [false, e.message];
  }
}
/**
 * @description
 * account 2 is me the user
 * account 1 is smartpay the receiver of the payment
 */
/* =============== 1) Balance =============== done*/

async function Balance(accountNumber) {
  //INFO: they share 2 acount to test, but idk if on prod only is gonna be one. ignore this, after i know the answer i will delete
  let account = process.env.ACCOUNT1;
  const url = `${COREBK_URL}/v1/core-banking/balance/1/${account}`;
  const [ok, out] = await callApi("GET", url);
  if (!ok) return fail(out);
  console.dir(out, { depth: null });
}

/* =============== 2) Dict Key =============== done*/

async function getKey(pixKey) {
  const url = `${BAAS_URL}/v2/pix/key-account-partner/1/${process.env.ACCOUNT1}/${pixKey}`;
  const [ok, out] = await callApi("GET", url);
  if (!ok) return fail(out);
  console.dir(out, { depth: null });
}

/* =============== DECODE QRCODE =============== done*/
async function decodeQrcode(qrcode) {
  const url = `https://gerador-arquitetura-baas.homolog.api.genial.systems/v1/pix/qrcode-decode?qrcode=${encodeURIComponent(
    qrcode
  )}`;
  const [ok, out] = await callApi("GET", url);
  if (!ok) return fail(out);
  console.dir(out, { depth: null });
}

/* =============== 8) Pay by QR =============== done*/

async function PayQr(qrCodeCopyPaste, value) {
  const url = `${BAAS_URL}/v1/pix/copy-paste`;
  /**
   * debit is our account that is going to pay the qr code, need prod keys to do it correctly
   */
  const body = {
    qrCodeCopyPaste,
    value: Number(value),
    debit: {
      name: process.env.ACCOUNT2_NAME,
      agency: 1,
      account: process.env.ACCOUNT2,
      accountType: "CACC",
    },
  };
  console.log(body);
  const [ok, out] = await callApi("POST", url, body);
  if (!ok) return fail(out);
  console.dir(out, { depth: null });
}

/* =============== 2) QR Code Static =============== done*/
async function qrcodeStatic() {
  const url = `${BAAS_URL}/v1/pix/qrcode-static/`;
  const body = {
    accountHolderName: process.env.ACCOUNT1_NAME,
    addressingKey: {
      key: process.env.ACCOUNT1_KEY,
      type: "EVP",
    },
    account: process.env.ACCOUNT1,
    agency: 1,
    // additionalInformation: "Pagamento de teste genial-cli" /**@optional */,
    // value: 10.0, /**@optional */
  };
  const [ok, out] = await callApi("POST", url, body);
  if (!ok) return fail(out);
  console.dir(out, { depth: null });
}

/* =============== 3) Status OUT (PAC008) =============== NEED TO CHECK*/
async function StatusOut(e2e) {
  const url = `${OPS_URL}/v1/cashout/${encodeURIComponent(e2e)}`;
  const [ok, out] = await callApi("GET", url);
  if (!ok) return fail(out);
  console.dir(out, { depth: null });
}

/* =============== 4) Status IN (PAC002) =============== NEED TO CHECK*/

async function StatusIn(transactionId) {
  const url = `${OPS_URL}/v1/cashin/end-to-end-id/${encodeURIComponent(
    transactionId
  )}`;
  const [ok, out] = await callApi("GET", url);
  if (!ok) return fail(out);
  console.dir(out, { depth: null });
}

/* =============== 6) Init Pix by Key (PAC008 Start) ===============  done */
/**
 *
 * @param pixKey
 * @param value
 * @returns confirmed idempotencyId [@description we need to create the idempotencyId on our side to confirm the payment later  ]
 */
async function PayInit(pixKey, value) {
  const url = `${BAAS_URL}/v1/pix/send/initialization`;
  const body = {
    debit: {
      name: process.env.ACCOUNT1_NAME,
      agency: 1,
      account: process.env.ACCOUNT1,
      accountType: "CACC",
    },
    credit: {
      key: pixKey,
    },
    value: String(value),
    idempotencyId: randomUUID(),
  };
  var [ok, out] = await callApi("POST", url, body);
  if (!ok) return fail(out);
  console.dir(out, { depth: null });
  if (out?.data?.idempotencyId) {
    console.log("idempotencyId:", out.data.idempotencyId);
  }
  var [ok, reply] = await paykey(out.data.idempotencyId);
  if (!ok) return fail(reply);
  console.dir(reply, { depth: null });
}

/* =============== 7) Pay by Idempotency (PAC008) =============== done */
/**
 *
 * @param idempotencyId
 * @param payinfo
 * @returns
 */
async function paykey(idempotencyId, payinfo) {
  const url = `${BAAS_URL}/v1/pix/send/confirmed`;
  const body = {
    idempotencyId: idempotencyId,
    ignorePaymentDuplication: false,
    // additionalInformation: payinfo, /**@optional */
  };
  const [ok, out] = await callApi("POST", url, body);
  if (!ok) return fail(out);
  if (DEBUG) console.log("Payment confirmed for idempotencyId:", idempotencyId);
  return [true, out];
}

/* =============== 9) Refund (PAC004) =============== worked*/

async function Refund(eventId, value) {
  const url = `${BAAS_URL}/v1/pix/return`;
  const body = {
    value: value,
    returnReasonCode: "MD06",
    eventId: eventId,
  };
  const [ok, out] = await callApi("POST", url, body);
  if (!ok) return fail(out);
  console.dir(out, { depth: null });
}

async function QrCodeDynamic(value) {
  const url = `${BAAS_URL}/v2/qrcode/dynamic?agency=1&account=${process.env.ACCOUNT1}`;
  const body = {
    items: [
      {
        addressingKey: {
          key: process.env.ACCOUNT1_KEY,
          type: "EVP",
        },
        accountHolderName: process.env.ACCOUNT1_NAME,
        accountHolderCity: "SAO PAULO",
        dynamicQRCodeType: "IMMEDIATE",
        immediate: {
          expiration: 36000,
          paymentValue: {
            documentValue: value,
            showPaymentValueInQrCode: true,
            //"cashback": {
            //"cashbackType": "WITHDRAW",
            //"value": 0,
            //"allowValueChange": true,
            //"withdrawProviders": {
            //"agentModality": "string",
            //"serviceProvider": "string"
            //}
            //}
          },
          payerInformation: {
            cpfCnpj: "37812511871",
            name: "gustavo lopes",
            validatePayerInformation: true,
          },
          receiverInformation: {
            taxId: process.env.DOCUMENT_NUMBER,
            name: process.env.ACCOUNT1_NAME,
            tradeName: process.env.ACCOUNT1_NAME,
          },
        },
        payerRequestInformation: "TESTE SOLICITAÇÃO AO PAYER",
        //"additionalInformation": [
        //{
        //"name": "string",
        //"content": "string",
        //"showToPayer": true
        //}
        //],
        //"itemId": "string",
        //"receiverTxId": "CTXXXXXXXXXXXX999999995800"
      },
    ],
  };
  const [ok, out] = await callApi("POST", url, body);
  if (!ok) return fail(out);
  console.dir(out, { depth: null });
}
/* =============== 10) Preview Pagamento (BOLETO) =============== did not test*/

async function Preview(emv) {
  const url = `${BAAS_URL}/v1/payments/preview`;
  const body = { emv };
  const [ok, out] = await callApi("POST", url, body);
  if (!ok) return fail(out);
  console.dir(out, { depth: null });
}

/* =============== 11) Boleto Pay =============== did not test*/

async function BoletoPay(taxId, line, description, uuid) {
  const url = `${BAAS_URL}/v1/payments/boleto`;
  const body = {
    taxId,
    description: description || "Pagamento de boleto",
    line,
    uuid: uuid || `BOL-${Date.now()}`,
  };
  const [ok, out] = await callApi("POST", url, body);
  if (!ok) return fail(out);
  console.dir(out, { depth: null });
}

/* =============== 12) Boleto Get by Id =============== did not test*/

async function BoletoGet(id) {
  const url = `${BAAS_URL}/v1/payments/boleto/${encodeURIComponent(id)}`;
  const [ok, out] = await callApi("GET", url);
  if (!ok) return fail(out);
  console.dir(out, { depth: null });
}

main();