import assert from "node:assert/strict";
import { afterEach, describe, test } from "node:test";
import { generateOrderId } from "./orderId";
import { formatAmount, amountsEqual } from "./money";
import {
  callbackChecksum,
  checkoutChecksum,
  ipayCheckoutUrl,
  nestedHmacBase64,
  normalizeChecksum,
  parseCallbackBody,
  webhookSignature,
} from "./ipay";

const original = {
  NODE_ENV: process.env.NODE_ENV,
  IPAY_CHECKOUT_URL: process.env.IPAY_CHECKOUT_URL,
};

afterEach(() => {
  if (original.NODE_ENV === undefined) delete process.env.NODE_ENV;
  else process.env.NODE_ENV = original.NODE_ENV;
  if (original.IPAY_CHECKOUT_URL === undefined) delete process.env.IPAY_CHECKOUT_URL;
  else process.env.IPAY_CHECKOUT_URL = original.IPAY_CHECKOUT_URL;
});

describe("iPay checksums", () => {
  test("nests HMAC-SHA256 and then Base64-encodes the outer digest", () => {
    assert.equal(nestedHmacBase64("secret", "Message"), "+VYktprBaoLLqMSnLNtFAW1kLdeAUdQUpsXaHtWb7fU=");
  });

  test("builds the checkout checksum from token, order id, and 2-decimal amount", () => {
    assert.equal(
      checkoutChecksum("sec_test", "tok_test", "OSLB1", "10.00"),
      "ckSKop07OUjdLivWUL+AA5H4ldruVXXQ0SBIXcRKFtI="
    );
  });

  test("builds the callback checksum from the documented field order", () => {
    const payload = {
      transactionReference: "REF1",
      orderId: "OSLB1",
      transactionTimeInMillis: "1710000000000",
      transactionAmount: "10.00",
      transactionStatus: "A",
      transactionMessage: "",
      merchantParam1: "",
      merchantParam2: "",
      checksum: "",
      creditedAmount: "",
    };
    const again = callbackChecksum("sec_test", payload);
    assert.equal(again, nestedHmacBase64("sec_test", "REF1OSLB1171000000000010.00A"));
    assert.equal(webhookSignature("key", "{\"ok\":true}"), webhookSignature("key", "{\"ok\":true}"));
  });

  test("keeps the exact amount digits from a JSON callback body", () => {
    const raw = '{"transactionAmount":"4999.00","transactionTimeInMillis":1710000000000,"orderId":"OSLB1"}';
    const parsed = parseCallbackBody("application/json", raw, JSON.parse(raw));
    assert.equal(parsed.transactionAmount, "4999.00");
    assert.equal(parsed.transactionTimeInMillis, "1710000000000");
  });

  test("restores Base64 plus signs that form decoding turned into spaces", () => {
    assert.equal(normalizeChecksum("ab cd+ef"), "ab+cd+ef");
  });
});

describe("checkout URL", () => {
  test("uses the configured URL, otherwise sandbox or live from NODE_ENV", () => {
    process.env.IPAY_CHECKOUT_URL = "https://sandbox.ipay.lk/ipg/checkout";
    assert.equal(ipayCheckoutUrl(), "https://sandbox.ipay.lk/ipg/checkout");

    process.env.IPAY_CHECKOUT_URL = "";
    process.env.NODE_ENV = "production";
    assert.equal(ipayCheckoutUrl(), "https://ipay.lk/ipg/checkout");

    process.env.NODE_ENV = "development";
    assert.equal(ipayCheckoutUrl(), "https://sandbox.ipay.lk/ipg/checkout");
  });
});

describe("amounts and order ids", () => {
  test("formats money to 2 decimals within iPay's 10 character limit", () => {
    assert.equal(formatAmount(4999), "4999.00");
    assert.equal(formatAmount("10.5"), "10.50");
    assert.equal(amountsEqual("4999.00", "4999"), true);
    assert.equal(amountsEqual("4999.00", "5000.00"), false);
    assert.throws(() => formatAmount(0));
    assert.throws(() => formatAmount("10000000.00"));
  });

  test("prefixes generated order ids for the product", () => {
    const orderId = generateOrderId("OSLB");
    assert.match(orderId, /^OSLB[A-Z0-9]+$/);
    assert.ok(orderId.length <= 100);
  });
});
