import { expect } from "chai";
import { v1ProductToBasePrice } from "tests/utils/testProductUtils/testProductUtils.js";
import { AutumnCli } from "../../cli/AutumnCli.js";
import { features, products } from "../../global.js";
import { compareMainProduct } from "../../utils/compare.js";
import { advanceClockForInvoice } from "../../utils/stripeUtils.js";
import { timeout } from "../../utils/genUtils.js";
import { calculateMetered1Price } from "@/external/stripe/utils.js";
import chalk from "chalk";
import { Customer } from "@autumn/shared";
import { setupBefore } from "tests/before.js";
import { initCustomer } from "@/utils/scriptUtils/initCustomer.js";
import Stripe from "stripe";

const testCase = "usage1";

describe(`${chalk.yellowBright("usage1: Testing basic usage product")}`, () => {
  const NUM_EVENTS = 50;
  const customerId = testCase;
  let testClockId: string;
  let customer: Customer;
  let stripeCli: Stripe;

  before(async function () {
    await setupBefore(this);
    stripeCli = this.stripeCli;

    const { customer: customer_, testClockId: testClockId_ } =
      await initCustomer({
        customerId,
        org: this.org,
        env: this.env,
        db: this.db,
        autumn: this.autumnJs,
        attachPm: "success",
      });

    customer = customer_;
    testClockId = testClockId_;
  });

  it("should attach usage based product", async function () {
    await AutumnCli.attach({
      customerId: customerId,
      productId: products.proWithOverage.id,
    });

    const res = await AutumnCli.getCustomer(customerId);

    compareMainProduct({
      sent: products.proWithOverage,
      cusRes: res,
    });
  });

  it("usage1: should send metered1 events", async function () {
    const batchUpdates = [];
    for (let i = 0; i < NUM_EVENTS; i++) {
      batchUpdates.push(
        AutumnCli.sendEvent({
          customerId: customerId,
          eventName: features.metered1.eventName,
        }),
      );
    }

    await Promise.all(batchUpdates);
    await timeout(10000);
  });

  it("should have correct metered1 balance after sending events", async function () {
    const res: any = await AutumnCli.entitled(customerId, features.metered1.id);

    expect(res!.allowed).to.be.true;

    const balance = res!.balances.find(
      (balance: any) => balance.feature_id === features.metered1.id,
    );

    const proOverageAmt =
      products.proWithOverage.entitlements.metered1.allowance;

    expect(res!.allowed, "should be allowed").to.be.true;

    expect(balance?.balance, "should have correct metered1 balance").to.equal(
      proOverageAmt! - NUM_EVENTS,
    );

    expect(balance?.usage_allowed, "should have usage_allowed").to.be.true;
  });

  // Check invoice
  it("should advance stripe test clock and wait for event", async function () {
    await advanceClockForInvoice({
      stripeCli,
      testClockId,
      waitForMeterUpdate: true,
    });
  });

  it("should have correct invoice amount", async function () {
    const cusRes = await AutumnCli.getCustomer(customerId);
    const invoices = cusRes!.invoices;

    // calculate price
    const price = calculateMetered1Price({
      product: products.proWithOverage,
      numEvents: NUM_EVENTS,
      metered1Feature: features.metered1,
    });

    expect(invoices.length).to.equal(2);

    const invoice2 = invoices[0];

    const basePrice = v1ProductToBasePrice({
      prices: products.proWithOverage.prices,
    });

    expect(invoice2.total).to.equal(
      price + basePrice,
      "invoice total should be usage price + base price",
    );
  });
});
