import { expect } from "e2e";
import { spawn } from "node:child_process";
import { copyFile, mkdir, writeFile, glob, readFile } from "node:fs/promises";
import { createServer } from "node:net";
import { APP_DIR, T3_HOME } from "../e2e.config.ts";
import { test, WEB } from "./t3.ts";

// Fresh server state only; never seed from an installed app.
test("Pairing a copied environment ID explains the conflict and preserves the code", WEB,
  async ({ t3, app, screen, browser }) => {
    const scratch = `${process.env.PAIRING_PROOF_SCRATCH}/run-${Date.now()}`;
    const home = `${scratch}/host-b`;
    await mkdir(`${home}/userdata`, { recursive: true });
    await copyFile(`${T3_HOME}/userdata/environment-id`, `${home}/userdata/environment-id`);
    const port = await new Promise<number>((resolve) => {
      const server = createServer();
      server.listen(0, "127.0.0.1", () => {
        const address = server.address();
        if (!address || typeof address === "string") throw new Error("No port");
        server.close(() => resolve(address.port));
      });
    });
    const child = spawn(process.execPath, ["apps/server/src/bin.ts", "serve", "--base-dir", home, "--port", String(port)], {
      cwd: APP_DIR,
      env: { ...process.env, T3CODE_DEV_SEED: "0", PATH: `${T3_HOME}/bin:${process.env.PATH}`, T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD: "0" },
      stdio: ["ignore", "pipe", "pipe"],
    });
    let output = "";
    child.stderr.on("data", chunk => { output += chunk; });
    try {
      const pairingUrl = await new Promise<string>((resolve, reject) => {
        child.stdout.on("data", chunk => {
          output += chunk;
          const match = /(?:pairingUrl|Pairing URL):\s*(\S+)/.exec(output);
          if (match) resolve(match[1]!);
        });
        child.on("exit", code => reject(new Error(`Second server exited ${code}`)));
        child.on("error", reject);
      });
      const descriptor = await fetch(`http://127.0.0.1:${port}/.well-known/t3/environment`).then(r => r.json());
      await t3.open();
      await screen.getByRole("button", "Start", { exact: true }).tap();
      await screen.getByRole("button", "Settings", { exact: true }).waitFor();
      await app.open("/settings/connections");
      await browser.evaluate(() => {
        const style = document.createElement("style");
        style.textContent = 'input[placeholder="PAIRCODE"] { -webkit-text-security: disc !important; }';
        document.head.append(style);
        return true;
      });
      await screen.getByRole("button", "Add environment").tap();
      await screen.getByRole("textbox", "Host", { exact: true }).fill(pairingUrl);
      await screen.getByRole("dialog").getByRole("button", "Add environment").tap();
      // Wait for either real outcome, so both commits get the same post-interaction capture.
      await browser.evaluate(() => new Promise<boolean>(resolve => {
        const check = () => {
          const text = document.body.innerText;
          if (text.includes("Backend added") || text.includes("That machine reports the same environment ID")) {
            observer.disconnect(); resolve(true);
          }
        };
        const observer = new MutationObserver(check);
        observer.observe(document.body, { childList: true, subtree: true, characterData: true });
        check();
      }));
      // Manual capture rule: require two identical PNGs at least 500 ms apart.
      let previous: Buffer | undefined;
      let stable = false;
      for (let attempt = 0; attempt < 8; attempt++) {
        await new Promise(resolve => setTimeout(resolve, 500));
        const relative = await app.screenshot(`settling-${attempt}`);
        const matches = await Array.fromAsync(glob(`${process.env.PAIRING_PROOF_OUTPUT}/results/**/${relative.split("/").at(-1)}`));
        const current = await readFile(matches[0]!);
        if (previous?.equals(current)) { stable = true; break; }
        previous = current;
      }
      if (!stable) throw new Error("The pairing result did not settle for capture");
      await app.screenshot("pairing-result");
      const credential = new URL(pairingUrl).hash.slice(1);
      const token = new URLSearchParams(credential).get("token")!;
      const response = await fetch(`http://127.0.0.1:${port}/oauth/token`, {
        method: "POST",
        body: new URLSearchParams({
          grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
          subject_token: token,
          subject_token_type: "urn:t3:params:oauth:token-type:environment-bootstrap",
          requested_token_type: "urn:ietf:params:oauth:token-type:access_token",
        }),
      });
      await writeFile(`${scratch}/outcome.json`, JSON.stringify({
        duplicateEnvironmentId: descriptor.environmentId,
        tokenExchangeAfterUI: response.status,
        ui: await browser.evaluate(() => document.body.innerText),
      }, null, 2));
      await expect(screen.getByText("That machine reports the same environment ID", { exact: false }).first()).toBeVisible();
      expect(response.status).toBe(200);
    } finally {
      child.kill("SIGTERM");
      if (child.exitCode === null) await new Promise<void>(resolve => child.once("exit", () => resolve()));
    }
  });
