import { expect } from "e2e";
import { spawn, execFileSync } from "node:child_process";
import { mkdirSync, mkdtempSync, writeFileSync, openSync } from "node:fs";
import { networkInterfaces, tmpdir } from "node:os";
import { createServer } from "node:net";
import { APP_DIR } from "../e2e.config.ts";
import { test, WEB } from "./t3.ts";

const reservePort = () => new Promise<number>((resolve) => {
  const server = createServer();
  server.listen(0, "0.0.0.0", () => {
    const port = (server.address() as { port: number }).port;
    server.close(() => resolve(port));
  });
});

test("A paired private IPv4 HTTP environment can be linked", WEB, async ({ t3, app, screen, browser }) => {
  await t3.project("LAN demonstration");
  await t3.open();
  await screen.getByRole("button", "Add project").waitFor();
  const clientOrigin = await browser.evaluate(() => location.origin);
  await browser.addInitScript(() => {
    new MutationObserver(() => {
      for (const input of document.querySelectorAll('input[placeholder="PAIRCODE"]')) {
        if ((input as HTMLInputElement).type !== "password") (input as HTMLInputElement).type = "password";
      }
    }).observe(document, { childList: true, subtree: true, attributes: true, attributeFilter: ["type"] });
  });
  const scratch = mkdtempSync(`${tmpdir()}/t3-lan-proof-`);
  const home = `${scratch}/peer`;
  mkdirSync(`${home}/userdata`, { recursive: true });
  mkdirSync(`${home}/bin`);
  writeFileSync(`${home}/userdata/settings.json`, JSON.stringify({ linkedEnvironmentsEnabled: true }));
  writeFileSync(`${home}/bin/hostnamectl`, '#!/bin/sh\necho "LAN peer"\n', { mode: 0o755 });
  const ip = Object.values(networkInterfaces()).flat().find((a) => a?.family === "IPv4" && !a.internal && /^(192\.168\.|10\.)/.test(a.address))!.address;
  const port = await reservePort();
  const origin = `http://${ip}:${port}`;
  const fd = openSync(`${scratch}/peer.log`, "w");
  const peer = spawn(process.execPath, ["scripts/dev-runner.ts", "dev:server", "--home-dir", home, "--no-seed", "--host", ip, "--port", String(port)], {
    cwd: APP_DIR, detached: true, stdio: ["ignore", fd, fd],
    env: { ...process.env, PATH: `${home}/bin:${process.env.PATH}`, T3CODE_DEV_SEED: "0", T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD: "0", T3CODE_DEV_AUTH_TOKEN: "", T3CODE_DEV_ALLOWED_ORIGINS: clientOrigin },
  });
  try {
    const deadline = Date.now() + 180_000;
    while (true) {
      try { if ((await fetch(`${origin}/.well-known/t3/environment`)).ok) break; } catch {}
      if (Date.now() > deadline) throw new Error("LAN peer server did not become ready");
      await new Promise((resolve) => setTimeout(resolve, 500));
    }
    const pairing = JSON.parse(execFileSync(process.execPath, ["apps/server/src/bin.ts", "auth", "pairing", "create", "--base-dir", home, "--json"], { cwd: APP_DIR, encoding: "utf8" }));
    await app.open("/settings/labs");
    await screen.getByRole("switch", "Linked environments").tap();
    await app.open("/settings/connections");
    await screen.getByRole("button", "Add environment").tap();
    await screen.getByLabel("Host").fill(origin);
    await browser.evaluate(() => {
      for (const input of document.querySelectorAll('input[placeholder="PAIRCODE"]')) input.setAttribute("type", "password");
    });
    await screen.getByLabel("Pairing code").fill(pairing.credential);
    await screen.getByRole("dialog").getByRole("button", "Add environment").tap();
    await screen.getByRole("dialog", "Add Environment").waitFor({ state: "hidden" });
    await screen.getByText("· Connected", { exact: false }).waitFor();
    await app.screenshot("client-paired-over-lan-http");
    await screen.getByRole("button", "Link", { exact: true }).first().tap();
    await screen.getByRole("combobox", "Machine").tap();
    await screen.getByRole("option", "LAN peer").tap();
    await screen.getByRole("dialog").getByRole("button", "Link", { exact: true }).tap();
    // Wait for the real request to settle before capturing either outcome.
    await screen.getByRole("button", "Linking…").waitFor({ state: "hidden" });
    if (await screen.getByRole("heading", "Could not link the environment").isVisible()) {
      await screen.getByRole("heading", "Could not link the environment").hover();
    }
    await app.screenshot("lan-http-link-result");
    await expect(screen.getByRole("dialog", "Link an environment")).toBeHidden();
    await expect(screen.getByText("Supervised ·", { exact: false })).toBeVisible();
    await app.screenshot("lan-http-link-reachable");
  } finally {
    if (peer.pid) process.kill(-peer.pid, "SIGTERM");
  }
});
