import { afterEach, describe, expect, it, vi } from "vitest";
import { createActor, waitFor } from "xstate";

import { PaymentPlansResponse } from "@/data/dto/payment";

import {
  createTestPaymentMachine,
  lifetimePlan,
  monthlyPlan,
  quarterlyPlan,
  tipPlan,
} from "./payment-machine.test-utils";

describe("payment catalog flow", () => {
  afterEach(() => vi.restoreAllMocks());

  it("loads plans and selects the monthly VIP plan", async () => {
    const actor = createActor(createTestPaymentMachine()).start();
    actor.send({ type: "PaymentInit" });
    await waitFor(actor, (snapshot) => snapshot.matches("ready"));

    expect(actor.getSnapshot().context).toMatchObject({
      selectedPlanId: "vip_monthly",
      autoRenew: true,
      errorMessage: null,
    });
    actor.stop();
  });

  it("loads the tip catalog and disables auto renew", async () => {
    const loadCatalog = vi.fn();
    const refreshCatalog = vi.fn();
    const actor = createActor(
      createTestPaymentMachine({
        refreshedPlans: PaymentPlansResponse.from({ plans: [tipPlan] }),
        onLoadCatalog: loadCatalog,
        onRefreshCatalog: refreshCatalog,
      }),
    ).start();
    actor.send({ type: "PaymentInit", catalog: "tip" });
    await waitFor(actor, (snapshot) => snapshot.matches("ready"));

    expect(loadCatalog).toHaveBeenCalledWith("tip");
    expect(refreshCatalog).toHaveBeenCalledWith("tip");
    expect(actor.getSnapshot().context).toMatchObject({
      planCatalog: "tip",
      selectedPlanId: tipPlan.planId,
      autoRenew: false,
    });
    actor.stop();
  });

  it("keeps first recharge plan metadata", async () => {
    const actor = createActor(
      createTestPaymentMachine({
        refreshedPlans: firstRechargeResponse(),
      }),
    ).start();
    actor.send({ type: "PaymentInit" });
    await waitFor(actor, (snapshot) => snapshot.matches("ready"));

    expect(actor.getSnapshot().context.isFirstRecharge).toBe(true);
    expect(actor.getSnapshot().context.plans[0]).toMatchObject({
      amountCents: 995,
      originalAmountCents: 1990,
      isFirstRechargeOffer: true,
    });
    actor.stop();
  });

  it("consumes first recharge state", async () => {
    const actor = createActor(
      createTestPaymentMachine({ refreshedPlans: firstRechargeResponse() }),
    ).start();
    actor.send({ type: "PaymentInit" });
    await waitFor(actor, (snapshot) => snapshot.matches("ready"));
    actor.send({ type: "PaymentFirstRechargeConsumed" });

    expect(actor.getSnapshot().context.isFirstRecharge).toBe(false);
    expect(actor.getSnapshot().context.plans[0]).toMatchObject({
      amountCents: 1990,
      originalAmountCents: 1990,
      isFirstRechargeOffer: false,
      firstRechargeDiscountPercent: null,
      promotionType: null,
    });
    actor.stop();
  });

  it("preserves normal VIP original price outside first recharge", async () => {
    const actor = createActor(
      createTestPaymentMachine({
        refreshedPlans: PaymentPlansResponse.from({
          isFirstRecharge: false,
          plans: [
            {
              ...monthlyPlan,
              amountCents: 1990,
              originalAmountCents: 2499,
            },
          ],
        }),
      }),
    ).start();
    actor.send({ type: "PaymentInit" });
    await waitFor(actor, (snapshot) => snapshot.matches("ready"));

    expect(actor.getSnapshot().context.plans[0]).toMatchObject({
      amountCents: 1990,
      originalAmountCents: 2499,
      isFirstRechargeOffer: false,
    });
    actor.stop();
  });

  it("hydrates cached plans before applying network plans", async () => {
    let resolveRefresh!: (value: PaymentPlansResponse) => void;
    const refreshPromise = new Promise<PaymentPlansResponse>((resolve) => {
      resolveRefresh = resolve;
    });
    const actor = createActor(
      createTestPaymentMachine({
        cachedPlans: PaymentPlansResponse.from({ plans: [quarterlyPlan] }),
        refreshPlans: async () => refreshPromise,
      }),
    ).start();
    actor.send({ type: "PaymentInit" });
    await waitFor(actor, (snapshot) => snapshot.matches("refreshingPlans"));
    expect(actor.getSnapshot().context.plans[0]?.planId).toBe(
      quarterlyPlan.planId,
    );

    resolveRefresh(
      PaymentPlansResponse.from({ plans: [monthlyPlan, lifetimePlan] }),
    );
    await waitFor(actor, (snapshot) => snapshot.matches("ready"));
    expect(actor.getSnapshot().context.selectedPlanId).toBe("vip_monthly");
    actor.stop();
  });
});

function firstRechargeResponse(): PaymentPlansResponse {
  return PaymentPlansResponse.from({
    isFirstRecharge: true,
    firstRechargeOffer: {
      enabled: true,
      type: "first_recharge_half_price",
      discountPercent: 50,
    },
    plans: [
      {
        ...monthlyPlan,
        amountCents: 995,
        originalAmountCents: 1990,
        isFirstRechargeOffer: true,
        firstRechargeDiscountPercent: 50,
        promotionType: "first_recharge_half_price",
      },
    ],
  });
}
