import * as p from "@clack/prompts";
import { bin, install } from "cloudflared";
import fs from "node:fs";
import { execSync } from "node:child_process";

export const CLOUDFLARED_VERSION = "2026.5.2";
export const GITHUB_CLOUDFLARED_URL =
  "https://api.github.com/repos/cloudflare/cloudflared/releases/latest";
export const TUNNEL_TIMEOUT = 30000;

/**
 * Fetches the latest cloudflared version from GitHub
 */
export async function getLatestVersion(): Promise<string | null> {
  try {
    const response = await fetch(GITHUB_CLOUDFLARED_URL);

    if (!response.ok) {
      throw new Error(`GitHub responded with status: ${response.status}`);
    }

    const data = (await response.json()) as { tag_name: string };
    return data.tag_name;
  } catch (error) {
    const errorMessage = error instanceof Error ? error.message : String(error);
    p.log.error(`Error fetching latest Cloudflared version: ${errorMessage}`);
    return null;
  }
}

/**
 * Compares two semantic version strings
 * @returns 1 if v1 > v2, -1 if v1 < v2, 0 if equal
 */
export function compareVersion(v1: string, v2: string): number {
  const parts1 = v1.split(".").map(Number);
  const parts2 = v2.split(".").map(Number);

  for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
    const p1 = parts1[i] || 0;
    const p2 = parts2[i] || 0;

    if (p1 > p2) return 1;
    if (p1 < p2) return -1;
  }

  return 0;
}

/**
 * Parses the version number from cloudflared version output
 */
export function parseVersion(versionOutput: string): string | null {
  const versionMatch = versionOutput.match(/version (\d+\.\d+\.\d+)/);
  return versionMatch?.[1] ?? null;
}

/**
 * Checks if cloudflared is installed and up-to-date, installs or updates if necessary
 */
export async function installCloudflared(): Promise<void> {
  const targetVersion = (await getLatestVersion()) || CLOUDFLARED_VERSION;
  let needsInstall = false;

  if (!fs.existsSync(bin)) {
    p.log.info("Cloudflared not found. Installing...");
    needsInstall = true;
  } else {
    try {
      const cloudflaredVersionOutput = execSync(`"${bin}" --version`)
        .toString()
        .trim();
      p.log.info(`Current ${cloudflaredVersionOutput}`);

      const currentVersion = parseVersion(cloudflaredVersionOutput);

      if (!currentVersion) {
        p.log.info("Unable to get current version. Will attempt to update.");
        needsInstall = true;
      } else if (compareVersion(targetVersion, currentVersion) > 0) {
        p.log.info(
          `Cloudflared version ${currentVersion} is older than ${targetVersion}. Updating...`
        );
        needsInstall = true;
      } else {
        p.log.info(`Using Cloudflared version ${currentVersion}`);
      }
    } catch (error) {
      const errorMessage =
        error instanceof Error ? error.message : String(error);
      p.log.error(`Error checking Cloudflared version: ${errorMessage}`);
      needsInstall = true;
    }
  }

  if (needsInstall) {
    const s = p.spinner();
    s.start(`Installing Cloudflared ${targetVersion}...`);

    try {
      await install(bin, targetVersion);
      s.stop(`Cloudflared ${targetVersion} installed successfully.`);
    } catch (error) {
      const errorMessage =
        error instanceof Error ? error.message : String(error);
      s.stop(`Failed to install Cloudflared: ${errorMessage}`);
      p.log.error(`Error installing Cloudflared: ${errorMessage}`);
      process.exit(1);
    }
  }
}
