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

import {
  PaymentPlansResponse,
  PaymentPlansResponseSchema,
} from "@/data/schemas/payment";

import {
  createTestPaymentMachine,
  lifetimePlan,
  monthlyPlan,
  quarterlyPlan,
} 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 loadGiftProducts = vi.fn();
    const actor = createActor(
      createTestPaymentMachine({
        onLoadGiftProducts: loadGiftProducts,
      }),
    ).start();
    actor.send({ type: "PaymentInit", catalog: "tip", characterId: "elio" });
    await waitFor(actor, (snapshot) => snapshot.matches("ready"));

    expect(loadGiftProducts).toHaveBeenCalledWith("elio");
    expect(actor.getSnapshot().context).toMatchObject({
      planCatalog: "tip",
      selectedGiftCategory: "coffee",
      selectedPlanId: "tip_coffee_usd_4_99",
      autoRenew: false,
    });
    expect(actor.getSnapshot().context.plans).toHaveLength(2);
    actor.stop();
  });

  it("restores a valid tip product across categories from checkout handoff", async () => {
    const actor = createActor(createTestPaymentMachine()).start();
    actor.send({
      type: "PaymentInit",
      catalog: "tip",
      characterId: "elio",
      category: "coffee",
      planId: "tip_coffee_usd_9_99",
    });
    await waitFor(actor, (snapshot) => snapshot.matches("ready"));
    expect(actor.getSnapshot().context.selectedPlanId).toBe(
      "tip_coffee_usd_9_99",
    );

    actor.send({
      type: "PaymentInit",
      catalog: "tip",
      characterId: "elio",
      category: "flowers",
      planId: "tip_flowers_usd_12_99",
    });
    await waitFor(
      actor,
      (snapshot) =>
        snapshot.matches("ready") &&
        snapshot.context.requestedGiftPlanId === null,
    );
    expect(actor.getSnapshot().context.selectedPlanId).toBe(
      "tip_flowers_usd_12_99",
    );
    expect(actor.getSnapshot().context.selectedGiftCategory).toBe("flowers");
    actor.stop();
  });

  it("clears gift prices after a catalog request fails and supports retry", async () => {
    const actor = createActor(
      createTestPaymentMachine({
        giftProductsError: new Error("catalog unavailable"),
      }),
    ).start();
    actor.send({ type: "PaymentInit", catalog: "tip", characterId: "elio" });
    await waitFor(actor, (snapshot) => snapshot.matches("ready"));

    expect(actor.getSnapshot().context).toMatchObject({
      plans: [],
      giftProducts: [],
      selectedPlanId: "",
      errorMessage: "catalog unavailable",
    });
    actor.send({ type: "PaymentCatalogRetryRequested" });
    await waitFor(actor, (snapshot) => snapshot.matches("loadingGiftProducts"));
    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: PaymentPlansResponseSchema.parse({
          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: PaymentPlansResponseSchema.parse({
          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(
      PaymentPlansResponseSchema.parse({ plans: [monthlyPlan, lifetimePlan] }),
    );
    await waitFor(actor, (snapshot) => snapshot.matches("ready"));
    expect(actor.getSnapshot().context.selectedPlanId).toBe("vip_monthly");
    actor.stop();
  });
});

function firstRechargeResponse(): PaymentPlansResponse {
  return PaymentPlansResponseSchema.parse({
    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",
      },
    ],
  });
}
