import { assert, expect } from "chai";
import { AutumnCli } from "../../cli/AutumnCli.js";
import { advanceProducts, creditSystems, features } from "../../global.js";
import { compareMainProduct } from "../../utils/compare.js";
import { advanceClockForInvoice } from "../../utils/stripeUtils.js";
import { timeout } from "../../utils/genUtils.js";
import { createStripeCli } from "@/external/stripe/utils.js";
import { Decimal } from "decimal.js";

import {
  checkUsageInvoiceAmount,
  getCreditsUsed,
} from "../../utils/advancedUsageUtils.js";

import chalk from "chalk";
import { setupBefore } from "tests/before.js";
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
import Stripe from "stripe";

// FIRST, REGULAR CHECK GPU STARTER MONTHLY

const testCase = "usage2";
describe(`${chalk.yellowBright("usage2: Testing basic usage product")}`, () => {
  const customerId = testCase;
  const PRECISION = 10;
  const ASSERT_INVOICE_AMOUNT = true;
  const CREDIT_MULTIPLIER = 100000;

  let testClockId = "";
  let totalCreditsUsed = 0;

  let stripeCli: Stripe;

  before(async function () {
    await setupBefore(this);
    const { testClockId: createdTestClockId } = await initCustomer({
      customerId,
      org: this.org,
      env: this.env,
      db: this.db,
      autumn: this.autumnJs,
      attachPm: "success",
    });

    testClockId = createdTestClockId;

    stripeCli = this.stripeCli;
  });

  it("should attach gpu system starter", async function () {
    await AutumnCli.attach({
      customerId: customerId,
      productId: advanceProducts.gpuSystemStarter.id,
    });

    const res = await AutumnCli.getCustomer(customerId);
    compareMainProduct({
      sent: advanceProducts.gpuSystemStarter,
      cusRes: res,
    });
  });

  // Use up events
  it("should send events and have correct balance (up to 10 DP)", async function () {
    let eventCount = 20;

    const batchEvents = [];
    for (let i = 0; i < eventCount; i++) {
      let randomVal = new Decimal(Math.random().toFixed(PRECISION))
        .mul(CREDIT_MULTIPLIER)
        .mul(Math.random() > 0.2 ? 1 : -1)
        .toNumber();
      let gpuId = i % 2 == 0 ? features.gpu1.id : features.gpu2.id;

      let creditsUsed = getCreditsUsed(
        creditSystems.gpuCredits,
        gpuId,
        randomVal,
      );

      totalCreditsUsed = new Decimal(totalCreditsUsed)
        .plus(creditsUsed)
        .toNumber();

      batchEvents.push(
        AutumnCli.sendEvent({
          customerId: customerId,
          eventName: gpuId,
          properties: { value: randomVal },
        }),
      );
    }

    await Promise.all(batchEvents);

    await timeout(10000);

    const { allowed, balanceObj }: any = await AutumnCli.entitled(
      customerId,
      creditSystems.gpuCredits.id,
      true,
    );

    let creditAllowance =
      advanceProducts.gpuSystemStarter.entitlements.gpuCredits.allowance!;

    expect(allowed).to.be.true;
    expect(balanceObj!.balance).to.equal(
      new Decimal(creditAllowance).minus(totalCreditsUsed).toNumber(),
    );
    // console.log("   - Total credits used: ", totalCreditsUsed);
    // console.log("   - Balance: ", balanceObj!.balance);
  });

  // Check invoice.created event
  it("should have correct invoice amount / updated meter balance", async function () {
    await advanceClockForInvoice({
      stripeCli,
      testClockId,
      waitForMeterUpdate: ASSERT_INVOICE_AMOUNT,
    });

    const res = await AutumnCli.getCustomer(customerId);
    const invoices = res!.invoices;

    if (ASSERT_INVOICE_AMOUNT) {
      await checkUsageInvoiceAmount({
        invoices,
        totalUsage: totalCreditsUsed,
        product: advanceProducts.gpuSystemStarter,
        featureId: creditSystems.gpuCredits.id,
      });
    } else {
      const { allowed, balanceObj }: any = await AutumnCli.entitled(
        customerId,
        creditSystems.gpuCredits.id,
        true,
      );

      let allowance =
        advanceProducts.gpuSystemStarter.entitlements.gpuCredits.allowance!;

      assert.equal(balanceObj.balance, allowance);
    }
  });
});
