import {expect, test} from "@playwright/test";

import {cameraFile, imageFile, pointFile} from "../binary_fixture";

test("initializes the local-only viewer shell", async ({page}) => {
  await page.goto("/tests/viewer.html");
  await expect(page.getByRole("heading", {name: "Open a COLMAP reconstruction"})).toBeVisible();
  await expect(page.getByRole("button", {name: "Choose folder"})).toBeVisible();
  await expect(page.locator('[data-viewer="canvas"]')).toBeAttached();
  await expect(page.locator('[data-viewer="title"]')).toHaveText("COLMAP - 3D Web Viewer");
  await expect(page.locator('[data-viewer="stats"]')).toBeHidden();

  // The point needs a track of at least 3 observations to pass the viewer's default track length filter.
  const files = await Promise.all([cameraFile(), imageFile(), pointFile(2, 42n, 3)].map(async (file) => ({
    name: file.name,
    mimeType: "application/octet-stream",
    buffer: Buffer.from(await file.arrayBuffer()),
  })));
  const input = page.locator('[data-viewer="folder-input"]');
  await input.evaluate((element) => element.removeAttribute("webkitdirectory"));
  await input.setInputFiles(files);
  await expect(page.locator('[data-viewer="stats"]')).toHaveText("1 images / 1 visible points");
  await expect(page.getByRole("heading", {name: "Model loaded"})).toBeVisible();

  const drop = page.locator('[data-viewer="drop"]');
  await drop.evaluate((element) => element.closest(".colmap-viewer-host")!.dispatchEvent(new DragEvent("dragenter", {bubbles: true})));
  await expect(drop).toBeVisible();
  await drop.evaluate((element) => element.closest(".colmap-viewer-host")!.dispatchEvent(new DragEvent("dragleave", {bubbles: true, relatedTarget: null})));
  await expect(drop).toBeHidden();

  await page.locator('[data-viewer="projection"]').selectOption("orthographic");
  await expect(page.locator('[data-viewer="projection"]')).toHaveValue("orthographic");

  const canvas = page.locator('[data-viewer="canvas"]');
  await canvas.evaluate((element) => element.dispatchEvent(new Event("webglcontextlost", {cancelable: true})));
  await expect(page.locator('[data-viewer="status"]')).toContainText("WebGL context lost");
  await canvas.evaluate((element) => element.dispatchEvent(new Event("webglcontextrestored")));
  await expect(page.locator('[data-viewer="status"]')).toBeHidden();

  const malformedFiles = files.map((file) => file.name === "cameras.bin" ? {...file, buffer: Buffer.from([1])} : file);
  await input.setInputFiles(malformedFiles);
  await expect(page.locator('[data-viewer="stats"]')).toBeHidden();
  await expect(page.getByRole("heading", {name: "Open a COLMAP reconstruction"})).toBeVisible();
  await expect(page.locator('[data-viewer="status"]')).toContainText("Failed to parse model");
  await expect(page.locator('[data-viewer="reset"]')).toBeDisabled();

  const lifecycle = await page.evaluate(async () => {
    const modulePath = "/viewer_src/main.ts";
    const {mountColmapViewer} = await import(/* @vite-ignore */ modulePath) as typeof import("../../viewer_src/main");
    const host = document.createElement("div");
    document.body.append(host);
    const embedded = mountColmapViewer(host, {title: "Embedded viewer"});
    const title = host.querySelector('[data-viewer="title"]')?.textContent;
    embedded.clear();
    embedded.dispose();
    return {title, childCount: host.childElementCount, hasHostClass: host.classList.contains("colmap-viewer-host")};
  });
  expect(lifecycle).toEqual({title: "Embedded viewer", childCount: 0, hasHostClass: false});
});
