// @vitest-environment jsdom
import { describe, expect, it, vi } from "vitest";

// ---------------------------------------------------------------------------
// Mock all lazy-loaded tool settings components so we don't pull in the
// entire React component tree. Each dynamic import returns a minimal stub.
// ---------------------------------------------------------------------------

vi.mock("@/components/tools/resize-settings", () => ({
  ResizeSettings: () => null,
}));
vi.mock("@/components/tools/crop-settings", () => ({
  CropSettings: () => null,
}));
vi.mock("@/components/tools/rotate-settings", () => ({
  RotateSettings: () => null,
}));
vi.mock("@/components/tools/convert-settings", () => ({
  ConvertSettings: () => null,
}));
vi.mock("@/components/tools/compress-settings", () => ({
  CompressSettings: () => null,
}));
vi.mock("@/components/tools/optimize-for-web-settings", () => ({
  OptimizeForWebSettings: () => null,
}));
vi.mock("@/components/tools/strip-metadata-settings", () => ({
  StripMetadataSettings: () => null,
}));
vi.mock("@/components/tools/edit-metadata-settings", () => ({
  EditMetadataSettings: () => null,
}));
vi.mock("@/components/tools/color-settings", () => ({
  ColorSettings: () => null,
}));
vi.mock("@/components/tools/sharpening-settings", () => ({
  SharpeningSettings: () => null,
}));
vi.mock("@/components/tools/watermark-text-settings", () => ({
  WatermarkTextSettings: () => null,
}));
vi.mock("@/components/tools/watermark-image-settings", () => ({
  WatermarkImageSettings: () => null,
}));
vi.mock("@/components/tools/text-overlay-settings", () => ({
  TextOverlaySettings: () => null,
}));
vi.mock("@/components/tools/compose-settings", () => ({
  ComposeSettings: () => null,
}));
vi.mock("@/components/tools/info-settings", () => ({
  InfoSettings: () => null,
}));
vi.mock("@/components/tools/compare-settings", () => ({
  CompareSettings: () => null,
}));
vi.mock("@/components/tools/find-duplicates-settings", () => ({
  FindDuplicatesSettings: () => null,
}));
vi.mock("@/components/tools/find-duplicates-results", () => ({
  FindDuplicatesResults: () => null,
}));
vi.mock("@/components/tools/color-palette-settings", () => ({
  ColorPaletteSettings: () => null,
}));
vi.mock("@/components/tools/qr-generate-settings", () => ({
  QrGenerateSettings: () => null,
}));
vi.mock("@/components/tools/qr-generate-preview", () => ({
  QrGeneratePreview: () => null,
}));
vi.mock("@/components/tools/barcode-read-settings", () => ({
  BarcodeReadSettings: () => null,
}));
vi.mock("@/components/tools/image-to-base64-settings", () => ({
  ImageToBase64Settings: () => null,
}));
vi.mock("@/components/tools/image-to-base64-results", () => ({
  ImageToBase64Results: () => null,
}));
vi.mock("@/components/tools/collage-settings", () => ({
  CollageSettings: () => null,
}));
vi.mock("@/components/tools/collage-preview", () => ({
  CollagePreview: () => null,
}));
vi.mock("@/components/tools/stitch-settings", () => ({
  StitchSettings: () => null,
}));
vi.mock("@/components/tools/split-settings", () => ({
  SplitSettings: () => null,
}));
vi.mock("@/components/tools/split-canvas", () => ({
  SplitCanvas: () => null,
}));
vi.mock("@/components/tools/border-settings", () => ({
  BorderSettings: () => null,
}));
vi.mock("@/components/tools/svg-to-raster-settings", () => ({
  SvgToRasterSettings: () => null,
}));
vi.mock("@/components/tools/vectorize-settings", () => ({
  VectorizeSettings: () => null,
}));
vi.mock("@/components/tools/gif-tools-settings", () => ({
  GifToolsSettings: () => null,
}));
vi.mock("@/components/tools/bulk-rename-settings", () => ({
  BulkRenameSettings: () => null,
}));
vi.mock("@/components/tools/favicon-settings", () => ({
  FaviconSettings: () => null,
}));
vi.mock("@/components/tools/image-to-pdf-settings", () => ({
  ImageToPdfSettings: () => null,
}));
vi.mock("@/components/tools/pdf-to-image-settings", () => ({
  PdfToImageSettings: () => null,
}));
vi.mock("@/components/tools/pdf-to-image-preview", () => ({
  PdfToImagePreview: () => null,
}));
vi.mock("@/components/tools/replace-color-settings", () => ({
  ReplaceColorSettings: () => null,
}));
vi.mock("@/components/tools/remove-bg-settings", () => ({
  RemoveBgSettings: () => null,
}));
vi.mock("@/components/tools/upscale-settings", () => ({
  UpscaleSettings: () => null,
}));
vi.mock("@/components/tools/ocr-settings", () => ({
  OcrSettings: () => null,
}));
vi.mock("@/components/tools/blur-faces-settings", () => ({
  BlurFacesSettings: () => null,
}));
vi.mock("@/components/tools/enhance-faces-settings", () => ({
  EnhanceFacesSettings: () => null,
}));
vi.mock("@/components/tools/erase-object-settings", () => ({
  EraseObjectSettings: () => null,
}));
vi.mock("@/components/tools/smart-crop-settings", () => ({
  SmartCropSettings: () => null,
}));
vi.mock("@/components/tools/image-enhancement-settings", () => ({
  ImageEnhancementSettings: () => null,
}));
vi.mock("@/components/tools/colorize-settings", () => ({
  ColorizeSettings: () => null,
}));
vi.mock("@/components/tools/noise-removal-settings", () => ({
  NoiseRemovalSettings: () => null,
}));
vi.mock("@/components/tools/passport-photo-settings", () => ({
  PassportPhotoSettings: () => null,
  PassportPhotoPreview: () => null,
}));
vi.mock("@/components/tools/red-eye-removal-settings", () => ({
  RedEyeRemovalSettings: () => null,
}));
vi.mock("@/components/tools/restore-photo-settings", () => ({
  RestorePhotoSettings: () => null,
}));
vi.mock("@/components/tools/transparency-fixer-settings", () => ({
  TransparencyFixerSettings: () => null,
}));
vi.mock("@/components/tools/content-aware-resize-settings", () => ({
  ContentAwareResizeSettings: () => null,
}));
vi.mock("@/components/tools/beautify-settings", () => ({
  BeautifySettings: () => null,
}));
vi.mock("@/components/tools/meme-generator-settings", () => ({
  MemeGeneratorSettings: () => null,
}));
vi.mock("@/components/tools/meme-generator-preview", () => ({
  MemeGeneratorPreview: () => null,
}));
vi.mock("@/components/tools/color-blindness-settings", () => ({
  ColorBlindnessSettings: () => null,
}));
vi.mock("@/components/tools/ai-canvas-expand-settings", () => ({
  AiCanvasExpandSettings: () => null,
}));
vi.mock("@/components/tools/merge-pdf-settings", () => ({
  MergePdfSettings: () => null,
}));
vi.mock("@/components/tools/split-pdf-settings", () => ({
  SplitPdfSettings: () => null,
}));
vi.mock("@/components/tools/compress-pdf-settings", () => ({
  CompressPdfSettings: () => null,
}));
vi.mock("@/components/tools/rotate-pdf-settings", () => ({
  RotatePdfSettings: () => null,
}));
vi.mock("@/components/tools/word-to-pdf-settings", () => ({
  WordToPdfSettings: () => null,
}));
vi.mock("@/components/tools/csv-excel-settings", () => ({
  CsvExcelSettings: () => null,
}));
vi.mock("@/components/tools/csv-json-settings", () => ({
  CsvJsonSettings: () => null,
}));
vi.mock("@/components/tools/json-xml-settings", () => ({
  JsonXmlSettings: () => null,
}));
vi.mock("@/components/tools/split-csv-settings", () => ({
  SplitCsvSettings: () => null,
}));

// Wave 5a: Image gap-fill tools
vi.mock("@/components/tools/barcode-generate-settings", () => ({
  BarcodeGenerateSettings: () => null,
}));
vi.mock("@/components/tools/chart-maker-settings", () => ({
  ChartMakerSettings: () => null,
}));
vi.mock("@/components/tools/circle-crop-settings", () => ({
  CircleCropSettings: () => null,
}));
vi.mock("@/components/tools/duotone-settings", () => ({
  DuotoneSettings: () => null,
}));
vi.mock("@/components/tools/gif-webp-settings", () => ({
  GifWebpSettings: () => null,
}));
vi.mock("@/components/tools/histogram-settings", () => ({
  HistogramSettings: () => null,
}));
vi.mock("@/components/tools/image-pad-settings", () => ({
  ImagePadSettings: () => null,
}));
vi.mock("@/components/tools/lqip-placeholder-settings", () => ({
  LqipPlaceholderSettings: () => null,
}));
vi.mock("@/components/tools/pixelate-settings", () => ({
  PixelateSettings: () => null,
}));
vi.mock("@/components/tools/sprite-sheet-settings", () => ({
  SpriteSheetSettings: () => null,
}));
vi.mock("@/components/tools/vignette-settings", () => ({
  VignetteSettings: () => null,
}));

// ---------------------------------------------------------------------------
// Import after mocks
// ---------------------------------------------------------------------------

import { TOOLS } from "@snapotter/shared";
import type { DisplayMode } from "@/lib/tool-registry";
import { getToolRegistryEntry, toolRegistry } from "@/lib/tool-registry";

// ==========================================================================
// toolRegistry (Map)
// ==========================================================================

describe("toolRegistry", () => {
  it("is a Map with entries", () => {
    expect(toolRegistry).toBeInstanceOf(Map);
    expect(toolRegistry.size).toBeGreaterThan(0);
  });

  it("contains all expected essential tool IDs", () => {
    const essentials = [
      "resize",
      "crop",
      "rotate",
      "convert",
      "compress",
      "strip-metadata",
      "edit-metadata",
    ];
    for (const id of essentials) {
      expect(toolRegistry.has(id), `missing tool: ${id}`).toBe(true);
    }
  });

  it("contains AI tool IDs", () => {
    const aiTools = [
      "remove-background",
      "upscale",
      "ocr",
      "blur-faces",
      "enhance-faces",
      "erase-object",
      "smart-crop",
      "image-enhancement",
      "colorize",
      "noise-removal",
      "passport-photo",
      "red-eye-removal",
      "restore-photo",
      "content-aware-resize",
    ];
    for (const id of aiTools) {
      expect(toolRegistry.has(id), `missing AI tool: ${id}`).toBe(true);
    }
  });

  it("contains layout and composition tools", () => {
    const layoutTools = ["collage", "stitch", "split", "border"];
    for (const id of layoutTools) {
      expect(toolRegistry.has(id), `missing layout tool: ${id}`).toBe(true);
    }
  });

  it("contains utility tools", () => {
    const utilityTools = [
      "info",
      "compare",
      "find-duplicates",
      "color-palette",
      "qr-generate",
      "barcode-read",
      "image-to-base64",
    ];
    for (const id of utilityTools) {
      expect(toolRegistry.has(id), `missing utility tool: ${id}`).toBe(true);
    }
  });

  it("contains format and conversion tools", () => {
    const formatTools = [
      "svg-to-raster",
      "vectorize",
      "gif-tools",
      "bulk-rename",
      "favicon",
      "image-to-pdf",
      "pdf-to-image",
      "optimize-for-web",
    ];
    for (const id of formatTools) {
      expect(toolRegistry.has(id), `missing format tool: ${id}`).toBe(true);
    }
  });

  it("every entry has a valid displayMode", () => {
    const validModes: DisplayMode[] = [
      "side-by-side",
      "before-after",
      "live-preview",
      "no-comparison",
      "interactive-crop",
      "interactive-eraser",
      "interactive-split",
      "no-dropzone",
      "custom-results",
      "media-player",
      "document",
    ];
    for (const [toolId, entry] of toolRegistry) {
      expect(validModes, `invalid displayMode for ${toolId}`).toContain(entry.displayMode);
    }
  });

  it("every entry has a Settings component", () => {
    for (const [toolId, entry] of toolRegistry) {
      expect(entry.Settings, `missing Settings for ${toolId}`).toBeDefined();
      expect(["function", "object"]).toContain(typeof entry.Settings);
    }
  });

  it("tools with custom-results display mode have a ResultsPanel", () => {
    for (const [toolId, entry] of toolRegistry) {
      if (entry.displayMode === "custom-results") {
        expect(entry.ResultsPanel, `missing ResultsPanel for ${toolId}`).toBeDefined();
      }
    }
  });

  it("tools with no-dropzone display mode have a ResultsPanel", () => {
    const selfContainedTools = new Set(["meme-generator", "barcode-generate", "chart-maker"]);
    for (const [toolId, entry] of toolRegistry) {
      if (entry.displayMode === "no-dropzone" && !selfContainedTools.has(toolId)) {
        expect(entry.ResultsPanel, `missing ResultsPanel for ${toolId}`).toBeDefined();
      }
    }
  });

  it("rotate has livePreview enabled", () => {
    const rotate = toolRegistry.get("rotate");
    expect(rotate?.livePreview).toBe(true);
  });

  it("adjust-colors has livePreview enabled", () => {
    const adjustColors = toolRegistry.get("adjust-colors");
    expect(adjustColors?.livePreview).toBe(true);
  });

  it("border has livePreview enabled", () => {
    const border = toolRegistry.get("border");
    expect(border?.livePreview).toBe(true);
  });

  it("beautify has livePreview enabled", () => {
    const beautify = toolRegistry.get("beautify");
    expect(beautify).toBeDefined();
    expect(beautify?.livePreview).toBe(true);
    expect(beautify?.displayMode).toBe("live-preview");
  });

  it("meme-generator has no-dropzone display mode with ResultsPanel", () => {
    const meme = toolRegistry.get("meme-generator");
    expect(meme).toBeDefined();
    expect(meme?.displayMode).toBe("no-dropzone");
    expect(meme?.ResultsPanel).toBeDefined();
    expect(meme?.Settings).toBeDefined();
  });

  it("color-blindness uses before-after display mode", () => {
    const cb = toolRegistry.get("color-blindness");
    expect(cb).toBeDefined();
    expect(cb?.displayMode).toBe("before-after");
    expect(cb?.Settings).toBeDefined();
  });

  it("transparency-fixer uses before-after display mode", () => {
    const tf = toolRegistry.get("transparency-fixer");
    expect(tf).toBeDefined();
    expect(tf?.displayMode).toBe("before-after");
    expect(tf?.Settings).toBeDefined();
  });

  it("crop uses interactive-crop display mode", () => {
    const crop = toolRegistry.get("crop");
    expect(crop?.displayMode).toBe("interactive-crop");
  });

  it("erase-object uses interactive-eraser display mode", () => {
    const eraseObject = toolRegistry.get("erase-object");
    expect(eraseObject?.displayMode).toBe("interactive-eraser");
  });

  it("split uses interactive-split display mode", () => {
    const split = toolRegistry.get("split");
    expect(split?.displayMode).toBe("interactive-split");
  });

  it("every tool in shared TOOLS[] has a matching registry entry", () => {
    const missing: string[] = [];
    for (const tool of TOOLS) {
      if (!toolRegistry.has(tool.id)) {
        missing.push(tool.id);
      }
    }
    expect(
      missing,
      `Tools defined in TOOLS[] but missing from toolRegistry: ${missing.join(", ")}`,
    ).toEqual([]);
  });
});

// ==========================================================================
// getToolRegistryEntry
// ==========================================================================

describe("getToolRegistryEntry", () => {
  it("returns the entry for a known tool", () => {
    const entry = getToolRegistryEntry("resize");
    expect(entry).toBeDefined();
    expect(entry?.displayMode).toBe("side-by-side");
  });

  it("returns undefined for an unknown tool", () => {
    expect(getToolRegistryEntry("nonexistent-tool")).toBeUndefined();
  });

  it("returns the correct entry for compress", () => {
    const entry = getToolRegistryEntry("compress");
    expect(entry).toBeDefined();
    expect(entry?.displayMode).toBe("before-after");
  });

  it("returns entry with ResultsPanel for find-duplicates", () => {
    const entry = getToolRegistryEntry("find-duplicates");
    expect(entry).toBeDefined();
    expect(entry?.displayMode).toBe("custom-results");
    expect(entry?.ResultsPanel).toBeDefined();
  });

  it("returns entry with ResultsPanel for qr-generate", () => {
    const entry = getToolRegistryEntry("qr-generate");
    expect(entry).toBeDefined();
    expect(entry?.displayMode).toBe("no-dropzone");
    expect(entry?.ResultsPanel).toBeDefined();
  });

  it("returns entry for content-aware-resize with side-by-side display (regression #131)", () => {
    const entry = getToolRegistryEntry("content-aware-resize");
    expect(entry).toBeDefined();
    expect(entry?.displayMode).toBe("side-by-side");
    expect(entry?.Settings).toBeDefined();
  });

  it("returns entry for beautify with live-preview display", () => {
    const entry = getToolRegistryEntry("beautify");
    expect(entry).toBeDefined();
    expect(entry?.displayMode).toBe("live-preview");
    expect(entry?.livePreview).toBe(true);
  });

  it("returns entry for meme-generator with ResultsPanel", () => {
    const entry = getToolRegistryEntry("meme-generator");
    expect(entry).toBeDefined();
    expect(entry?.displayMode).toBe("no-dropzone");
    expect(entry?.ResultsPanel).toBeDefined();
  });

  it("returns entry for color-blindness", () => {
    const entry = getToolRegistryEntry("color-blindness");
    expect(entry).toBeDefined();
    expect(entry?.displayMode).toBe("before-after");
  });

  it("no duplicate tool IDs in registry", () => {
    const ids = [...toolRegistry.keys()];
    const unique = new Set(ids);
    expect(ids.length).toBe(unique.size);
  });
});

// ==========================================================================
// Wrapper components (lines 305-324)
// ==========================================================================

import { render } from "@testing-library/react";
import React from "react";

function renderSettings(Settings: React.ComponentType<Record<string, unknown>>, props = {}) {
  return render(
    React.createElement(React.Suspense, { fallback: null }, React.createElement(Settings, props)),
  );
}

describe("CropSettingsWrapper", () => {
  it("renders null when cropProps is undefined", () => {
    const entry = getToolRegistryEntry("crop");
    expect(entry).toBeDefined();
    const { container } = renderSettings(entry?.Settings as never);
    expect(container.innerHTML).toBe("");
  });

  it("renders CropSettings when cropProps is provided", () => {
    const entry = getToolRegistryEntry("crop");
    expect(entry).toBeDefined();
    const cropProps = {
      cropState: {
        crop: { x: 0, y: 0, width: 50, height: 50, unit: "%" },
        aspect: undefined,
        showGrid: true,
        imgDimensions: { width: 200, height: 150 },
      },
      onCropChange: vi.fn(),
      onAspectChange: vi.fn(),
      onGridToggle: vi.fn(),
    };
    const { container } = renderSettings(entry?.Settings as never, { cropProps });
    expect(container).toBeDefined();
  });
});

describe("EraseObjectSettingsWrapper", () => {
  it("renders null when eraserProps is undefined", () => {
    const entry = getToolRegistryEntry("erase-object");
    expect(entry).toBeDefined();
    const { container } = renderSettings(entry?.Settings as never);
    expect(container.innerHTML).toBe("");
  });

  it("renders EraseObjectSettings when eraserProps is provided", () => {
    const entry = getToolRegistryEntry("erase-object");
    expect(entry).toBeDefined();
    const eraserProps = {
      eraserRef: React.createRef(),
      hasStrokes: false,
      brushSize: 20,
      onBrushSizeChange: vi.fn(),
    };
    const { container } = renderSettings(entry?.Settings as never, { eraserProps });
    expect(container).toBeDefined();
  });
});

describe("makeColorSettingsComponent", () => {
  it("adjust-colors Settings renders without throwing", () => {
    const entry = getToolRegistryEntry("adjust-colors");
    expect(entry).toBeDefined();
    const { container } = renderSettings(entry?.Settings as never, {
      onPreviewFilter: vi.fn(),
    });
    expect(container).toBeDefined();
  });
});
