#!/usr/bin/env bun

import * as p from "@clack/prompts";
import { Tunnel } from "cloudflared";
import { installCloudflared, TUNNEL_TIMEOUT } from "./utils";

const APP_VERSION = "5.5.0";
const DEFAULT_PORT = "30000";

async function main(): Promise<void> {
  console.log();
  p.intro(`[ FVTTT ${APP_VERSION} ]`);

  await installCloudflared();

  const port = await p.text({
    message: "FoundryVTT Port",
    placeholder: DEFAULT_PORT,
    initialValue: DEFAULT_PORT,
    validate: (value) => {
      const portNum = parseInt(value || DEFAULT_PORT, 10);
      if (isNaN(portNum) || portNum < 1 || portNum > 65535) {
        return "Please enter a valid port number (1-65535)";
      }
    },
  });

  if (p.isCancel(port)) {
    p.cancel("Operation cancelled");
    return process.exit(0);
  }

  const s = p.spinner();
  s.start("Waiting for tunnel URL...");

  const host = `http://localhost:${port || DEFAULT_PORT}`;
  const tunnel = Tunnel.quick(host);
  let exitCode = 0;
  let timeoutId: ReturnType<typeof setTimeout> | undefined;

  const stopTunnel = () => {
    if (timeoutId) {
      clearTimeout(timeoutId);
      timeoutId = undefined;
    }
    tunnel.stop();
  };

  tunnel.on("exit", (code) => {
    p.log.info(`Cloudflare tunnel exited with code ${code ?? 0}`);
    process.exit(exitCode || code || 0);
  });

  const shutdown = (message?: string) => {
    if (message) p.cancel(message);
    stopTunnel();
  };

  process.once("SIGINT", () => shutdown("Tunnel stopped"));
  process.once("SIGTERM", () => shutdown());

  try {
    const url = await new Promise<string>((resolve, reject) => {
      timeoutId = setTimeout(() => {
        reject(new Error("Timeout waiting for tunnel URL"));
      }, TUNNEL_TIMEOUT);

      tunnel.once("url", (url) => {
        if (timeoutId) clearTimeout(timeoutId);
        resolve(url);
      });

      tunnel.once("error", (error) => {
        if (timeoutId) clearTimeout(timeoutId);
        reject(error);
      });
    });

    s.stop(`Tunnel URL: ${url}`);
    p.outro(
      "Share the URL above with your players. Press Ctrl+C to stop the tunnel."
    );
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    s.stop(`Failed to establish tunnel: ${errorMessage}`);
    exitCode = 1;
    stopTunnel();
  }
}

main().catch((error) => {
  const errorMessage = error instanceof Error ? error.message : String(error);
  p.log.error(`Error: ${errorMessage}`);
  process.exit(1);
});
