import { describe, expect, it, vi } from "vitest";
import {
  executeVariantLoaders,
  resolvePreviewRuntimeState,
  type PreviewVariantLike,
} from "../preview-runtime.js";

describe("executeVariantLoaders", () => {
  it("returns host loadedData when no loaders are defined", async () => {
    const loadedData = { source: "host", count: 1 };
    const result = await executeVariantLoaders(undefined, loadedData);
    expect(result).toEqual(loadedData);
  });

  it("merges loader results and applies host loadedData last", async () => {
    const result = await executeVariantLoaders(
      [
        async () => ({ count: 1, fromFirst: true }),
        async () => ({ count: 2, fromSecond: true }),
      ],
      { count: 3, fromHost: true },
    );

    expect(result).toEqual({
      count: 3,
      fromFirst: true,
      fromSecond: true,
      fromHost: true,
    });
  });
});

describe("resolvePreviewRuntimeState", () => {
  it("renders variants with no loaders", async () => {
    const variant: PreviewVariantLike = {
      render: () => "Preview content",
    };

    const state = await resolvePreviewRuntimeState({ variant });

    expect(state.isLoading).toBe(false);
    expect(state.error).toBeNull();
    expect(state.content).toBe("Preview content");
  });

  it("renders with successful loaders", async () => {
    const variant: PreviewVariantLike = {
      loaders: [async () => ({ label: "from-loader" })],
      render: (options) => `Loaded: ${String(options?.loadedData?.label)}`,
    };

    const state = await resolvePreviewRuntimeState({ variant });

    expect(state.error).toBeNull();
    expect(state.loadedData).toEqual({ label: "from-loader" });
    expect(state.content).toBe("Loaded: from-loader");
  });

  it("returns an error when a loader rejects", async () => {
    const variant: PreviewVariantLike = {
      loaders: [async () => { throw new Error("loader failed"); }],
      render: () => "should not render",
    };

    const state = await resolvePreviewRuntimeState({ variant });

    expect(state.content).toBeNull();
    expect(state.error?.message).toContain("loader failed");
  });

  it("returns an error when render throws", async () => {
    const variant: PreviewVariantLike = {
      render: () => {
        throw new Error("render failed");
      },
    };

    const state = await resolvePreviewRuntimeState({ variant });

    expect(state.content).toBeNull();
    expect(state.error?.message).toContain("render failed");
  });

  it("normalizes non-Error throwables", async () => {
    const variant: PreviewVariantLike = {
      loaders: [async () => { throw "string-failure"; }],
      render: () => "never",
    };

    const state = await resolvePreviewRuntimeState({ variant });
    expect(state.error).not.toBeNull();
    expect(state.error?.message).toContain("string-failure");
  });

  it("uses host data as override input in render options", async () => {
    const renderSpy = vi.fn((options?: { loadedData?: Record<string, unknown> }) => options?.loadedData);
    const variant: PreviewVariantLike = {
      loaders: [async () => ({ count: 1 })],
      render: renderSpy,
    };

    const state = await resolvePreviewRuntimeState({
      variant,
      loadedData: { count: 2, source: "host" },
    });

    expect(renderSpy).toHaveBeenCalledTimes(1);
    expect(state.loadedData).toEqual({ count: 2, source: "host" });
    expect(state.content).toEqual({ count: 2, source: "host" });
  });
});
