import { cleanup, render } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";

import { renderFileDropControl } from "./controls-panel-media-renderer";
import type { ToolcraftSourceAssetCoordinator } from "../../../source-assets/source-asset-coordinator";

const legacyFileDrop = vi.hoisted(() =>
  vi.fn((_props: unknown, _legacyContext?: unknown) => null),
);

vi.mock("@repo/ui", async (importOriginal) => {
  const actual = await importOriginal<typeof import("@repo/ui")>();

  return {
    ...actual,
    FileDrop: legacyFileDrop,
  };
});

afterEach(() => {
  cleanup();
  legacyFileDrop.mockClear();
});

describe("controls-panel media renderer", () => {
  it("does not invoke the legacy uploader for model fileDrops", () => {
    const dispatchCommand = vi.fn();
    const idleOperation = {
      phase: "idle" as const,
      target: "source.model",
    };
    const sourceAssetCoordinator = {
      cancelTarget: vi.fn(),
      clearPresentationFeedback: vi.fn(),
      dispose: vi.fn(async () => undefined),
      getOperation: () => idleOperation,
      getPresentation: vi.fn(() => null),
      hydrateBinaryMedia: vi.fn(async () => undefined),
      hydrateModels: vi.fn(async () => undefined),
      importBatch: vi.fn(),
      repairModel: vi.fn(),
      reportPresentationFeedback: vi.fn(),
      subscribe: () => () => undefined,
    } satisfies ToolcraftSourceAssetCoordinator;
    const renderedControl = renderFileDropControl({
      canvasSize: { height: 720, unit: "px", width: 1280 },
      control: {
        assetKind: "model",
        target: "source.model",
        type: "fileDrop",
      },
      dispatchCommand,
      id: "source-model",
      mediaAssets: [],
      name: "Model",
      sourceAssetCoordinator,
    });

    const { container } = render(<>{renderedControl}</>);

    expect(container.firstChild).toBeNull();
    expect(legacyFileDrop).not.toHaveBeenCalled();
    expect(dispatchCommand).not.toHaveBeenCalled();
  });

  it("uses the coordinator presentation delegated by the registered handler", () => {
    const control = {
      assetKind: "file" as const,
      target: "source.files",
      type: "fileDrop" as const,
    };
    const operation = { phase: "idle" as const, target: control.target };
    const presentation = {
      accept: ".txt",
      allowsFileBatch: true,
      allowsFolderSelection: false,
      emptyDescription: "Drop files",
      emptyTitle: "Upload files",
      items: [],
      presenter: "file-list" as const,
      secondaryActions: [],
    };
    const getPresentation = vi.fn(() => presentation);
    const sourceAssetCoordinator = {
      cancelTarget: vi.fn(),
      clearPresentationFeedback: vi.fn(),
      dispose: vi.fn(async () => undefined),
      getOperation: vi.fn(() => operation),
      getPresentation,
      hydrateBinaryMedia: vi.fn(async () => undefined),
      hydrateModels: vi.fn(async () => undefined),
      importBatch: vi.fn(),
      repairModel: vi.fn(),
      reportPresentationFeedback: vi.fn(),
      subscribe: () => () => undefined,
    } satisfies ToolcraftSourceAssetCoordinator;

    render(
      <>
        {renderFileDropControl({
          canvasSize: { height: 720, unit: "px", width: 1280 },
          control,
          dispatchCommand: vi.fn(),
          id: "source-files",
          mediaAssets: [],
          name: "Source files",
          sourceAssetCoordinator,
        })}
      </>,
    );

    expect(getPresentation).toHaveBeenCalledWith(control, [], operation);
    expect(legacyFileDrop).toHaveBeenCalledWith(
      expect.objectContaining({ presentation }),
      undefined,
    );
    expect(legacyFileDrop.mock.calls[0]?.[0]).toEqual(
      expect.objectContaining({
        collectionActions: undefined,
        onFolderSelect: undefined,
      }),
    );
  });

  it("maps the collection-actions variant to compact FileDrop cardinality controls", () => {
    const control = {
      assetKind: "file" as const,
      itemLabel: "bump map",
      multiple: true,
      target: "surface.bumpMaps",
      type: "fileDrop" as const,
      variant: "collection-actions",
    };
    const operation = { phase: "idle" as const, target: control.target };
    const presentation = {
      accept: "image/*",
      allowsFileBatch: true,
      allowsFolderSelection: false,
      emptyDescription: "Drop bump maps",
      emptyTitle: "Upload bump maps",
      items: [{
        assetKind: "file" as const,
        fileName: "grain.png",
        id: "bump-grain",
      }],
      presenter: "file-list" as const,
      secondaryActions: [],
    };
    const sourceAssetCoordinator = {
      cancelTarget: vi.fn(),
      clearPresentationFeedback: vi.fn(),
      dispose: vi.fn(async () => undefined),
      getOperation: vi.fn(() => operation),
      getPresentation: vi.fn(() => presentation),
      hydrateBinaryMedia: vi.fn(async () => undefined),
      hydrateModels: vi.fn(async () => undefined),
      importBatch: vi.fn(),
      repairModel: vi.fn(),
      reportPresentationFeedback: vi.fn(),
      subscribe: () => () => undefined,
    } satisfies ToolcraftSourceAssetCoordinator;

    render(
      <>
        {renderFileDropControl({
          canvasSize: { height: 720, unit: "px", width: 1280 },
          control,
          dispatchCommand: vi.fn(),
          id: "surface-bump-maps",
          mediaAssets: [],
          name: "Bump maps",
          sourceAssetCoordinator,
        })}
      </>,
    );

    expect(legacyFileDrop.mock.calls[0]?.[0]).toEqual(
      expect.objectContaining({
        collectionActions: {
          addLabel: "Add bump map",
          canAdd: true,
          itemLabel: "bump map",
          name: "Bump maps",
          removeLabel: "Remove bump map",
        },
      }),
    );
  });

  it("routes model file and folder picks through one coordinator import path", () => {
    const control = {
      assetKind: "model" as const,
      target: "source.model",
      type: "fileDrop" as const,
    };
    const operation = { phase: "idle" as const, target: control.target };
    const presentation = {
      accept: ".gltf,.glb",
      allowsFileBatch: true,
      allowsFolderSelection: true,
      emptyDescription: "Drop a model",
      emptyTitle: "Upload model",
      items: [],
      presenter: "model-item" as const,
      secondaryActions: [],
    };
    const importBatch = vi.fn();
    const sourceAssetCoordinator = {
      cancelTarget: vi.fn(),
      clearPresentationFeedback: vi.fn(),
      dispose: vi.fn(async () => undefined),
      getOperation: vi.fn(() => operation),
      getPresentation: vi.fn(() => presentation),
      hydrateBinaryMedia: vi.fn(async () => undefined),
      hydrateModels: vi.fn(async () => undefined),
      importBatch,
      repairModel: vi.fn(),
      reportPresentationFeedback: vi.fn(),
      subscribe: () => () => undefined,
    } satisfies ToolcraftSourceAssetCoordinator;

    render(
      <>
        {renderFileDropControl({
          canvasSize: { height: 720, unit: "px", width: 1280 },
          control,
          dispatchCommand: vi.fn(),
          id: "source-model",
          mediaAssets: [],
          name: "Model",
          sourceAssetCoordinator,
        })}
      </>,
    );

    const fileDropProps = legacyFileDrop.mock.calls[0]?.[0] as unknown as {
      onFilesSelect: (files: File[]) => void;
      onFolderSelect: (files: File[]) => void;
    };
    const files = [new File([new Uint8Array([1])], "scene.gltf")];

    expect(fileDropProps.onFolderSelect).toBe(fileDropProps.onFilesSelect);
    fileDropProps.onFolderSelect(files);
    expect(importBatch).toHaveBeenCalledWith(
      { files, origin: "panel", target: "source.model" },
      control,
    );
  });

  it("routes the built-in model repair action to the source coordinator", () => {
    const control = {
      assetKind: "model" as const,
      target: "source.model",
      type: "fileDrop" as const,
    };
    const operation = { phase: "idle" as const, target: control.target };
    const presentation = {
      accept: ".glb",
      allowsFileBatch: true,
      allowsFolderSelection: true,
      emptyDescription: "Drop a model",
      emptyTitle: "Upload model",
      items: [{
        assetKind: "model" as const,
        fileName: "scene.glb",
        id: "model-1",
      }],
      presenter: "model-item" as const,
      secondaryActions: [{
        label: "Fix model",
        value: "model.fix",
      }],
    };
    const repairModel = vi.fn(async () => ({
      assetIds: ["model-1"],
      kind: "committed" as const,
    }));
    const sourceAssetCoordinator = {
      cancelTarget: vi.fn(),
      clearPresentationFeedback: vi.fn(),
      dispose: vi.fn(async () => undefined),
      getOperation: vi.fn(() => operation),
      getPresentation: vi.fn(() => presentation),
      hydrateBinaryMedia: vi.fn(async () => undefined),
      hydrateModels: vi.fn(async () => undefined),
      importBatch: vi.fn(),
      repairModel,
      reportPresentationFeedback: vi.fn(),
      subscribe: () => () => undefined,
    } satisfies ToolcraftSourceAssetCoordinator;

    render(
      <>
        {renderFileDropControl({
          canvasSize: { height: 720, unit: "px", width: 1280 },
          control,
          dispatchCommand: vi.fn(),
          id: "source-model",
          mediaAssets: [],
          name: "Model",
          sourceAssetCoordinator,
        })}
      </>,
    );

    const fileDropCalls = legacyFileDrop.mock.calls as unknown as Array<[
      { onAction?: (value: string) => void },
    ]>;
    const onAction = fileDropCalls[0]?.[0].onAction;
    expect(onAction).toBeTypeOf("function");
    onAction?.("model.fix");
    expect(repairModel).toHaveBeenCalledWith("model-1");
  });
});
