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

import type {
  PrivateZonePostUnlockResponse,
  PrivateZonePostsResponse,
} from "@/data/schemas/private-zone";

import { privateZonePostMachine } from "../private-zone-post-machine";

const POST_ID = "11111111-1111-1111-1111-111111111111";
const lockedPost = {
  postId: POST_ID,
  characterId: "maya-tan",
  caption: "Only for you.",
  posterUrl: "https://storage.example/poster.jpg?token=short",
  mediaType: "video" as const,
  videoUrl: null,
  videoMimeType: "video/mp4",
  videoSizeBytes: 1024,
  durationSeconds: 18,
  unlockCostCredits: 100,
  currency: "credits" as const,
  locked: true,
  unlocked: false,
  availableForNewUnlock: true,
  publishedAt: "2026-07-24T10:00:00+00:00",
};

const listResponse: PrivateZonePostsResponse = {
  characterId: "maya-tan",
  items: [lockedPost],
  nextCursor: null,
  hasMore: false,
  creditBalance: 150,
  currency: "credits",
};

function actorWithUnlock(response: PrivateZonePostUnlockResponse) {
  const machine = privateZonePostMachine.provide({
    actors: {
      loadPosts: fromPromise(async () => listResponse),
      unlockPost: fromPromise(async () => response),
    },
  });
  return createActor(machine, { input: { characterId: "maya-tan" } }).start();
}

describe("Private Zone video post machine", () => {
  it("loads moments independently from album state", async () => {
    const actor = actorWithUnlock({
      postId: POST_ID,
      reason: "deduct_failed",
      unlocked: false,
      creditsCharged: 0,
      requiredCredits: 100,
      currentCredits: 150,
      shortfallCredits: 0,
      creditBalance: 150,
      post: lockedPost,
    });

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

    expect(actor.getSnapshot().context.items).toHaveLength(1);
    expect(actor.getSnapshot().context.creditBalance).toBe(150);
  });

  it("patches the signed video after a successful unlock", async () => {
    const actor = actorWithUnlock({
      postId: POST_ID,
      reason: "ok",
      unlocked: true,
      creditsCharged: 100,
      requiredCredits: 100,
      currentCredits: 50,
      shortfallCredits: 0,
      creditBalance: 50,
      post: {
        ...lockedPost,
        locked: false,
        unlocked: true,
        videoUrl: "https://storage.example/video.mp4?token=short",
      },
    });

    actor.send({ type: "PrivateZonePostInit" });
    await waitFor(actor, (snapshot) => snapshot.matches("ready"));
    actor.send({ type: "PrivateZonePostUnlockRequested", postId: POST_ID });
    actor.send({ type: "PrivateZonePostUnlockConfirmed" });
    await waitFor(
      actor,
      (snapshot) => snapshot.matches("ready") && snapshot.context.unlockSuccessNonce === 1,
    );

    expect(actor.getSnapshot().context.items[0].videoUrl).toContain("video.mp4");
    expect(actor.getSnapshot().context.creditBalance).toBe(50);
  });

  it("creates a top-up request without unlocking when credits are insufficient", async () => {
    const actor = actorWithUnlock({
      postId: POST_ID,
      reason: "insufficient_credits",
      unlocked: false,
      creditsCharged: 0,
      requiredCredits: 100,
      currentCredits: 25,
      shortfallCredits: 75,
      creditBalance: 25,
      post: lockedPost,
    });

    actor.send({ type: "PrivateZonePostInit" });
    await waitFor(actor, (snapshot) => snapshot.matches("ready"));
    actor.send({ type: "PrivateZonePostUnlockRequested", postId: POST_ID });
    actor.send({ type: "PrivateZonePostUnlockConfirmed" });
    await waitFor(
      actor,
      (snapshot) => snapshot.matches("ready") && !!snapshot.context.unlockPaywallRequest,
    );

    expect(actor.getSnapshot().context.unlockPaywallRequest?.shortfallCredits).toBe(75);
    expect(actor.getSnapshot().context.items[0].videoUrl).toBeNull();
  });
});
