import type { Express } from "express";
import { execCommand } from "../widgets/common-utils.js";
import { execElevated } from "./exec-elevated.js";
import { managerHandler, ManagerInputError } from "./route-helpers.js";
import type { ManagerRoutesDeps } from "./types.js";
import { isValidTailscaleAction } from "./validation.js";

export interface TailscalePeer {
  hostname: string;
  tailscaleIPs: string[];
  online: boolean;
  isExitNode: boolean;
}

export interface TailscaleData {
  installed: boolean;
  running: boolean;
  tailscaleIPs: string[];
  hostname: string | null;
  peers: TailscalePeer[];
  exitNodeInUse: boolean;
}

const PROBE_CMD = [
  "command -v tailscale >/dev/null 2>&1 && echo ts_installed=1 || echo ts_installed=0",
  "tailscale status --json 2>/dev/null",
].join("; ");

export function parseTailscaleData(output: string): TailscaleData {
  const notInstalled: TailscaleData = {
    installed: false,
    running: false,
    tailscaleIPs: [],
    hostname: null,
    peers: [],
    exitNodeInUse: false,
  };

  if (output.includes("ts_installed=0")) return notInstalled;

  // Strip the installation probe line to get the raw JSON
  const lines = output.split("\n");
  const jsonLines = lines.filter(
    (l) => !l.startsWith("ts_installed=") && l.trim() !== "",
  );
  const jsonStr = jsonLines.join("\n");

  try {
    const parsed = JSON.parse(jsonStr) as {
      BackendState?: string;
      Self?: { HostName?: string; TailscaleIPs?: string[] };
      Peer?: Record<
        string,
        {
          HostName?: string;
          TailscaleIPs?: string[];
          Online?: boolean;
          ExitNode?: boolean;
        }
      >;
      CurrentExitNode?: string;
    };

    const peers: TailscalePeer[] = Object.values(parsed.Peer ?? {}).map(
      (p) => ({
        hostname: p.HostName ?? "",
        tailscaleIPs: p.TailscaleIPs ?? [],
        online: p.Online ?? false,
        isExitNode: p.ExitNode ?? false,
      }),
    );

    return {
      installed: true,
      running: parsed.BackendState === "Running",
      tailscaleIPs: parsed.Self?.TailscaleIPs ?? [],
      hostname: parsed.Self?.HostName ?? null,
      peers,
      exitNodeInUse:
        typeof parsed.CurrentExitNode === "string" &&
        parsed.CurrentExitNode !== "",
    };
  } catch {
    return {
      installed: true,
      running: false,
      tailscaleIPs: [],
      hostname: null,
      peers: [],
      exitNodeInUse: false,
    };
  }
}

export function registerTailscaleRoutes(
  app: Express,
  { validateHostId, runOnHost }: ManagerRoutesDeps,
): void {
  /**
   * @openapi
   * /host-metrics/managers/tailscale/{id}:
   *   get:
   *     summary: Get Tailscale status and IPs
   *     tags:
   *       - Host Metrics
   *     parameters:
   *       - in: path
   *         name: id
   *         required: true
   *         schema:
   *           type: integer
   *     responses:
   *       200:
   *         description: Tailscale installation status, running state, IPs, and peer count.
   */
  app.get(
    "/host-metrics/managers/tailscale/:id",
    validateHostId,
    managerHandler(runOnHost, "connect", "tailscale_read", async (client) => {
      const { stdout } = await execCommand(client, PROBE_CMD, 15000);
      return parseTailscaleData(stdout);
    }),
  );

  /**
   * @openapi
   * /host-metrics/managers/tailscale/{id}/action:
   *   post:
   *     summary: Connect or disconnect Tailscale
   *     tags:
   *       - Host Metrics
   *     parameters:
   *       - in: path
   *         name: id
   *         required: true
   *         schema:
   *           type: integer
   *     requestBody:
   *       required: true
   *       content:
   *         application/json:
   *           schema:
   *             type: object
   *             properties:
   *               action:
   *                 type: string
   *                 enum: [up, down]
   *     responses:
   *       200:
   *         description: Action result.
   */
  app.post(
    "/host-metrics/managers/tailscale/:id/action",
    validateHostId,
    managerHandler(
      runOnHost,
      "connect",
      "tailscale_action",
      async (client, host, req) => {
        const { action } = req.body as { action: unknown };
        if (!isValidTailscaleAction(action)) {
          throw new ManagerInputError("Invalid action, must be 'up' or 'down'");
        }
        const result = await execElevated(
          client,
          `tailscale ${action}`,
          host.sudoPassword,
          { forceSudo: false, timeoutMs: 30000 },
        );
        return {
          success: result.code === 0,
          output: (result.stdout + result.stderr).trim(),
        };
      },
    ),
  );
}
