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

import {
  PrivateRoomMomentsResponse,
  PrivateRoomUnlockResponse,
} from "@/data/dto/private-room";
import { privateRoomMachine } from "@/stores/private-room/private-room-machine";

const baseMoment = {
  momentId: "schedule:91",
  source: "elio_schedules",
  sourceId: "91",
  characterId: "elio",
  author: {
    id: "elio",
    name: "Elio Silvestri",
    avatarUrl: null,
  },
  createdAt: "2026-07-01T08:11:35+00:00",
  publishedAt: "2026-07-01T08:11:35+00:00",
  timeText: "7 days ago",
  title: "Paris morning",
  content: null,
  text: null,
  textPreview: "Unlock to view private room photos",
  mediaCount: 1,
  images: [
    {
      url: null,
      type: "image",
      locked: true,
      index: 0,
    },
  ],
  locked: true,
  unlocked: false,
  unlockCost: 40,
  unlockCostPerImage: 40,
  requiredCredits: 40,
  currency: "credits",
  lockDetail: {
    locked: true,
    showContent: false,
    showUpgrade: true,
    reason: "private_room_moment",
    hint: "40 credits · Unlock photo",
    actionLabel: "Unlock",
    type: "private_room_moment",
    requiredCredits: 40,
    currentCredits: 100,
    shortfallCredits: 0,
    mediaCount: 1,
    unlockCostPerImage: 40,
  },
};

function makeMomentsResponse(
  overrides: Partial<Parameters<typeof PrivateRoomMomentsResponse.from>[0]> = {},
): PrivateRoomMomentsResponse {
  return PrivateRoomMomentsResponse.from({
    profile: {
      characterId: "elio",
      displayName: "Elio Silvestri",
      authorName: "Elio Silvestri",
      avatarUrl: null,
      coverUrl: null,
      title: "Elio Private room",
      subtitle: "Join me, unlock my private room",
    },
    items: [baseMoment],
    nextCursor: null,
    hasMore: false,
    creditBalance: 100,
    unlockCostDefault: 40,
    unlockCostPerImage: 40,
    currency: "credits",
    source: "elio_schedules",
    ...overrides,
  });
}

function makeUnlockResponse(
  overrides: Partial<Parameters<typeof PrivateRoomUnlockResponse.from>[0]> = {},
): PrivateRoomUnlockResponse {
  return PrivateRoomUnlockResponse.from({
    ...baseMoment,
    locked: false,
    unlocked: true,
    images: [
      {
        url: "https://example.supabase.co/storage/v1/object/public/elio-schedules/photo.jpg",
        type: "image",
        locked: false,
        index: 0,
      },
    ],
    reason: "ok",
    creditsCharged: 40,
    creditBalance: 60,
    ...overrides,
  });
}

function createTestMachine(
  options: {
    firstPage?: PrivateRoomMomentsResponse;
    morePage?: PrivateRoomMomentsResponse;
    unlockResponse?: PrivateRoomUnlockResponse;
  } = {},
) {
  return privateRoomMachine.provide({
    actors: {
      loadMoments: fromPromise(async () => options.firstPage ?? makeMomentsResponse()),
      loadMoreMoments: fromPromise(async () =>
        options.morePage ??
        makeMomentsResponse({
          items: [
            {
              ...baseMoment,
              momentId: "schedule:92",
              sourceId: "92",
            },
          ],
          hasMore: false,
          nextCursor: null,
        }),
      ),
      unlockMoment: fromPromise(async () =>
        options.unlockResponse ?? makeUnlockResponse(),
      ),
    },
  });
}

describe("privateRoomMachine", () => {
  it("loads the first page", async () => {
    const actor = createActor(createTestMachine()).start();

    actor.send({ type: "PrivateRoomInit" });
    await waitFor(actor, (snapshot) => snapshot.matches("ready"));

    const context = actor.getSnapshot().context;
    expect(context.profile?.displayName).toBe("Elio Silvestri");
    expect(context.items).toHaveLength(1);
    expect(context.creditBalance).toBe(100);

    actor.stop();
  });

  it("appends more moments without overwriting existing items", async () => {
    const actor = createActor(
      createTestMachine({
        firstPage: makeMomentsResponse({
          hasMore: true,
          nextCursor: "cursor-1",
        }),
      }),
    ).start();

    actor.send({ type: "PrivateRoomInit" });
    await waitFor(actor, (snapshot) => snapshot.matches("ready"));

    actor.send({ type: "PrivateRoomLoadMore" });
    await waitFor(actor, (snapshot) => snapshot.matches("ready"));

    expect(actor.getSnapshot().context.items.map((item) => item.momentId)).toEqual([
      "schedule:91",
      "schedule:92",
    ]);

    actor.stop();
  });

  it("replaces a locked moment after unlock success", async () => {
    const actor = createActor(createTestMachine()).start();

    actor.send({ type: "PrivateRoomInit" });
    await waitFor(actor, (snapshot) => snapshot.matches("ready"));

    actor.send({
      type: "PrivateRoomUnlockRequested",
      momentId: "schedule:91",
    });
    actor.send({ type: "PrivateRoomUnlockConfirmed" });
    await waitFor(actor, (snapshot) => snapshot.matches("ready"));

    const context = actor.getSnapshot().context;
    expect(context.items[0]?.unlocked).toBe(true);
    expect(context.items[0]?.images[0]?.url).toContain("https://");
    expect(context.unlockSuccessNonce).toBe(1);

    actor.stop();
  });

  it("exposes a paywall request for insufficient credits", async () => {
    const actor = createActor(
      createTestMachine({
        unlockResponse: makeUnlockResponse({
          ...baseMoment,
          reason: "insufficient_credits",
          locked: true,
          unlocked: false,
          creditBalance: 10,
          currentCredits: 10,
          shortfallCredits: 30,
        }),
      }),
    ).start();

    actor.send({ type: "PrivateRoomInit" });
    await waitFor(actor, (snapshot) => snapshot.matches("ready"));

    actor.send({
      type: "PrivateRoomUnlockRequested",
      momentId: "schedule:91",
    });
    actor.send({ type: "PrivateRoomUnlockConfirmed" });
    await waitFor(actor, (snapshot) => snapshot.matches("ready"));

    const context = actor.getSnapshot().context;
    expect(context.items[0]?.unlocked).toBe(false);
    expect(context.unlockPaywallRequest).toMatchObject({
      momentId: "schedule:91",
      reason: "insufficient_credits",
      shortfallCredits: 30,
    });

    actor.stop();
  });

  it("refreshes and keeps an unlock error when the cost changes", async () => {
    const actor = createActor(
      createTestMachine({
        unlockResponse: makeUnlockResponse({
          ...baseMoment,
          reason: "cost_changed",
          locked: true,
          unlocked: false,
        }),
      }),
    ).start();

    actor.send({ type: "PrivateRoomInit" });
    await waitFor(actor, (snapshot) => snapshot.matches("ready"));

    actor.send({
      type: "PrivateRoomUnlockRequested",
      momentId: "schedule:91",
    });
    actor.send({ type: "PrivateRoomUnlockConfirmed" });
    await waitFor(actor, (snapshot) => snapshot.matches("ready"));

    expect(actor.getSnapshot().context.unlockErrorMessage).toBe(
      "Unlock price changed. Please confirm again.",
    );

    actor.stop();
  });
});
