import * as Net from "node:net";
import * as OS from "node:os";
import * as NodeServices from "@effect/platform-node/NodeServices";
import * as HostProcess from "@t3tools/shared/HostProcess";
import * as NetService from "@t3tools/shared/Net";
import { expect } from "e2e";
import * as Deferred from "effect/Deferred";
import * as Effect from "effect/Effect";
import * as Fiber from "effect/Fiber";
import * as Path from "effect/Path";

import { APP_DIR, T3_HOME } from "../e2e.config.ts";
import { prepareHome, start } from "./start-app.ts";
import { makeState, runPromise, test, WEB } from "./t3.ts";

const LAN = Object.values(OS.networkInterfaces()).flat().find((entry) => entry?.family === "IPv4" && entry.address.startsWith("192.168."))!.address;

const SECOND_HOME = `${T3_HOME}-second-environment`;

/** A second server on its own T3 home, running until interrupted. `--dev-url` admits the web
 * origin through CORS and registers the replay agent; `paired` gets the Add environment input. */
const runSecondServer = Effect.fn("scenario.secondServer")(function* (
  devUrl: string,
  paired: (target: { readonly host: string; readonly token: string }) => void,
) {
  const path = yield* Path.Path;
  const node = yield* HostProcess.ExecutablePath;
  const hostEnvironment = yield* HostProcess.Environment;
  const port = yield* (yield* NetService.NetService).reserveLoopbackPort();
  yield* prepareHome(SECOND_HOME);
  const token = yield* Deferred.make<string>();
  yield* start({
    cwd: APP_DIR,
    command: node,
    args: [
      "apps/server/src/bin.ts",
      "serve",
      "--host",
      LAN,
      "--port",
      String(port),
      "--base-dir",
      SECOND_HOME,
      "--dev-url",
      devUrl,
    ],
    env: {
      PATH: `${path.join(SECOND_HOME, "bin")}:${hostEnvironment.PATH ?? ""}`,
      HOME: hostEnvironment.HOME,
      T3CODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD: "0",
      T3CODE_DEV_SEED: "0",
    },
    onLine: (line) => {
      const value = /^Token:\s*(\S+)/.exec(line)?.[1];
      return value === undefined ? Effect.void : Deferred.succeed(token, value).pipe(Effect.asVoid);
    },
  });
  const code = yield* Deferred.await(token);
  const forward = Net.createServer((socket) => {
    const remote = Net.connect(port, LAN);
    socket.pipe(remote).pipe(socket);
    socket.on("error", () => remote.destroy());
    remote.on("error", () => socket.destroy());
  });
  yield* Effect.acquireRelease(
    Effect.promise(() => new Promise<void>((resolve) => forward.listen(0, "127.0.0.1", resolve))),
    () => Effect.sync(() => forward.close()),
  );
  const forwardedPort = (forward.address() as Net.AddressInfo).port;
  paired({ host: `http://127.0.0.1:${forwardedPort}`, token: code });
  return yield* Effect.never;
});

test("Picked machines with only refused HTTP LAN addresses expose an address field", WEB,
  async ({ t3, app, screen, browser }) => {
    await t3.project("demo");
    const pairing = Promise.withResolvers<{ host: string; token: string }>();
    const server = Effect.runFork(runSecondServer(app.baseUrl ?? "", pairing.resolve).pipe(
      Effect.scoped, Effect.provide([NodeServices.layer, NetService.layer]),
    ));
    server.addObserver(() => pairing.reject(new Error("The second server stopped.")));
    try {
      const { host, token } = await pairing.promise;
      await t3.open("/settings/labs");
      await screen.getByRole("switch", "Linked environments").check();
      await app.open("/settings/connections");
      await screen.getByRole("button", "Add environment").tap();
      let dialog = screen.getByRole("dialog");
      await dialog.getByPlaceholder("backend.example.com").fill(host);
      // Conceal the ephemeral credential in the recording without changing the pairing flow.
      await browser.evaluate(() => document.querySelector('input[placeholder="PAIRCODE"]')?.setAttribute("type", "password"));
      await dialog.getByPlaceholder("PAIRCODE").fill(token);
      await dialog.getByRole("button", "Add environment").tap();
      await screen.getByRole("button", "More actions for Scenario").hover();
      await screen.getByRole("button", "Link", { exact: true }).first().tap();
      dialog = screen.getByRole("dialog");
      await dialog.getByRole("combobox", "Machine").tap();
      await screen.getByRole("option", "Scenario", { exact: true }).tap();
      await app.screenshot("picked-machine");
      const submit = dialog.getByRole("button", "Link", { exact: true });
      const ready = await browser.evaluate(() => !document.querySelector<HTMLButtonElement>('[role="dialog"] button[type="submit"]')?.disabled);
      if (ready) {
        await submit.tap();
        await screen.getByText("Could not link the environment", { exact: true }).hover();
      }
      await app.screenshot("link-result");
      await expect(dialog.getByPlaceholder("https://box.example.ts.net")).toBeVisible();
    } finally {
      await Effect.runPromise(Fiber.interrupt(server));
    }
  });
