import { createServer, request as httpRequest } from "node:http";
import { request as httpsRequest } from "node:https";
import { realpathSync } from "node:fs";
import { pathToFileURL } from "node:url";

const DEFAULT_PORT = 9100;
const DEFAULT_TARGET_TIMEOUT_MS = 2000;
const DEFAULT_EXTRA_METRICS_MAX_BYTES = 64 * 1024;
const startedAtMs = Date.now();

export function sanitizeLabelValue(value, fallback = "unknown") {
  const text = String(value ?? "").trim();
  const sanitized = text.replace(/[^A-Za-z0-9_.:-]/gu, "_").slice(0, 120);
  return sanitized || fallback;
}

export function escapeLabelValue(value) {
  return String(value ?? "").replace(/\\/gu, "\\\\").replace(/\n/gu, "\\n").replace(/"/gu, "\\\"");
}

function metricLabels(env) {
  const service = sanitizeLabelValue(env.HWLAB_METRICS_SERVICE_ID || env.HWLAB_SERVICE_ID);
  const namespace = sanitizeLabelValue(env.HWLAB_METRICS_NAMESPACE || env.POD_NAMESPACE || "hwlab-v02");
  const gitopsTarget = sanitizeLabelValue(env.HWLAB_METRICS_GITOPS_TARGET || env.HWLAB_GITOPS_TARGET || "v02");
  return `service="${escapeLabelValue(service)}",namespace="${escapeLabelValue(namespace)}",gitops_target="${escapeLabelValue(gitopsTarget)}"`;
}

export function requestTarget({ targetUrl, timeoutMs = DEFAULT_TARGET_TIMEOUT_MS, collectBody = false, maxBodyBytes = DEFAULT_EXTRA_METRICS_MAX_BYTES } = {}) {
  const configured = typeof targetUrl === "string" && targetUrl.trim().length > 0;
  if (!configured) return Promise.resolve({ configured: false, success: false, statusCode: 0, durationSeconds: 0 });

  let url;
  try {
    url = new URL(targetUrl);
  } catch {
    return Promise.resolve({ configured: true, success: false, statusCode: 0, durationSeconds: 0 });
  }

  const requestImpl = url.protocol === "https:" ? httpsRequest : url.protocol === "http:" ? httpRequest : null;
  if (!requestImpl) return Promise.resolve({ configured: true, success: false, statusCode: 0, durationSeconds: 0 });

  const started = Date.now();
  return new Promise((resolve) => {
    const finish = (result) => resolve({
      configured: true,
      success: false,
      statusCode: 0,
      durationSeconds: (Date.now() - started) / 1000,
      ...result
    });
    const request = requestImpl(url, { method: "GET", timeout: timeoutMs }, (response) => {
      const chunks = [];
      let bytes = 0;
      if (collectBody) {
        response.on("data", (chunk) => {
          const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
          bytes += buffer.length;
          if (bytes <= maxBodyBytes) chunks.push(buffer);
        });
      } else {
        response.resume();
      }
      response.on("end", () => finish({
        success: Boolean(response.statusCode && response.statusCode >= 200 && response.statusCode < 300),
        statusCode: response.statusCode || 0,
        body: collectBody ? Buffer.concat(chunks).toString("utf8") : undefined,
        bodyBytes: collectBody ? Math.min(bytes, maxBodyBytes) : 0,
        truncated: collectBody ? bytes > maxBodyBytes : false
      }));
    });
    request.on("timeout", () => request.destroy());
    request.on("error", () => finish({}));
    request.end();
  });
}

export async function probeExtraMetrics({ targetUrl, timeoutMs = DEFAULT_TARGET_TIMEOUT_MS, maxBodyBytes = DEFAULT_EXTRA_METRICS_MAX_BYTES, probeImpl = requestTarget } = {}) {
  const configured = typeof targetUrl === "string" && targetUrl.trim().length > 0;
  if (!configured || typeof probeImpl !== "function") {
    return { configured: false, success: false, statusCode: 0, durationSeconds: 0, text: "" };
  }
  try {
    const result = await probeImpl({ targetUrl, timeoutMs, collectBody: true, maxBodyBytes });
    return {
      configured: true,
      success: Boolean(result.success),
      statusCode: result.statusCode || 0,
      durationSeconds: result.durationSeconds || 0,
      text: result.success ? sanitizeExtraMetricsText(result.body || "") : ""
    };
  } catch {
    return { configured: true, success: false, statusCode: 0, durationSeconds: 0, text: "" };
  }
}

export function sanitizeExtraMetricsText(text) {
  const lines = String(text ?? "").split(/\r?\n/gu);
  return lines.filter((line) => {
    if (!line) return true;
    if (/^#\s+(?:HELP|TYPE)\s+hwlab_[A-Za-z_:][A-Za-z0-9_:]*\b/u.test(line)) return true;
    return /^hwlab_[A-Za-z_:][A-Za-z0-9_:]*(?:\{[^\n{}]*\})?\s+[-+0-9.eE]+$/u.test(line);
  }).join("\n");
}

export async function probeTarget({ targetUrl, timeoutMs = DEFAULT_TARGET_TIMEOUT_MS, probeImpl = requestTarget } = {}) {
  const configured = typeof targetUrl === "string" && targetUrl.trim().length > 0;
  if (!configured || typeof probeImpl !== "function") {
    return { configured: false, success: false, statusCode: 0, durationSeconds: 0 };
  }
  try {
    return await probeImpl({ targetUrl, timeoutMs });
  } catch {
    const started = Date.now();
    return { configured: true, success: false, statusCode: 0, durationSeconds: (Date.now() - started) / 1000 };
  }
}

export function buildMetricsText({ env = process.env, probe = null, extraMetrics = null, now = Date.now() } = {}) {
  const labels = metricLabels(env);
  const uptimeSeconds = Math.max(0, (now - startedAtMs) / 1000);
  const healthProbe = probe ?? { configured: false, success: false, statusCode: 0, durationSeconds: 0 };
  const extraProbe = extraMetrics ?? { configured: false, success: false, statusCode: 0, durationSeconds: 0, text: "" };
  const lines = [
    "# HELP hwlab_service_up HWLAB metrics sidecar availability.",
    "# TYPE hwlab_service_up gauge",
    `hwlab_service_up{${labels}} 1`,
    "# HELP hwlab_service_info Static HWLAB service metrics identity without high-cardinality request identifiers.",
    "# TYPE hwlab_service_info gauge",
    `hwlab_service_info{${labels}} 1`,
    "# HELP hwlab_service_process_uptime_seconds Metrics sidecar process uptime in seconds.",
    "# TYPE hwlab_service_process_uptime_seconds gauge",
    `hwlab_service_process_uptime_seconds{${labels}} ${uptimeSeconds.toFixed(3)}`,
    "# HELP hwlab_service_health_probe_configured Whether the sidecar has a cluster-local health probe target.",
    "# TYPE hwlab_service_health_probe_configured gauge",
    `hwlab_service_health_probe_configured{${labels}} ${healthProbe.configured ? 1 : 0}`,
    "# HELP hwlab_service_health_probe_success Whether the most recent cluster-local health probe succeeded.",
    "# TYPE hwlab_service_health_probe_success gauge",
    `hwlab_service_health_probe_success{${labels}} ${healthProbe.success ? 1 : 0}`,
    "# HELP hwlab_service_health_probe_status_code Most recent cluster-local health probe HTTP status code, or 0 when unavailable.",
    "# TYPE hwlab_service_health_probe_status_code gauge",
    `hwlab_service_health_probe_status_code{${labels}} ${Number.isInteger(healthProbe.statusCode) ? healthProbe.statusCode : 0}`,
    "# HELP hwlab_service_health_probe_duration_seconds Most recent cluster-local health probe duration in seconds.",
    "# TYPE hwlab_service_health_probe_duration_seconds gauge",
    `hwlab_service_health_probe_duration_seconds{${labels}} ${Number(healthProbe.durationSeconds || 0).toFixed(3)}`,
    "# HELP hwlab_service_extra_metrics_configured Whether the sidecar has an additional cluster-local metrics target.",
    "# TYPE hwlab_service_extra_metrics_configured gauge",
    `hwlab_service_extra_metrics_configured{${labels}} ${extraProbe.configured ? 1 : 0}`,
    "# HELP hwlab_service_extra_metrics_success Whether the most recent additional metrics fetch succeeded.",
    "# TYPE hwlab_service_extra_metrics_success gauge",
    `hwlab_service_extra_metrics_success{${labels}} ${extraProbe.success ? 1 : 0}`,
    "# HELP hwlab_service_extra_metrics_status_code Most recent additional metrics HTTP status code, or 0 when unavailable.",
    "# TYPE hwlab_service_extra_metrics_status_code gauge",
    `hwlab_service_extra_metrics_status_code{${labels}} ${Number.isInteger(extraProbe.statusCode) ? extraProbe.statusCode : 0}`,
    "# HELP hwlab_service_extra_metrics_duration_seconds Most recent additional metrics fetch duration in seconds.",
    "# TYPE hwlab_service_extra_metrics_duration_seconds gauge",
    `hwlab_service_extra_metrics_duration_seconds{${labels}} ${Number(extraProbe.durationSeconds || 0).toFixed(3)}`,
    ""
  ];
  if (extraProbe.text) {
    lines.push(extraProbe.text.trimEnd(), "");
  }
  return lines.join("\n");
}

function sendJson(response, statusCode, body) {
  const payload = JSON.stringify(body) + "\n";
  response.writeHead(statusCode, {
    "content-type": "application/json; charset=utf-8",
    "content-length": Buffer.byteLength(payload)
  });
  response.end(payload);
}

export function createMetricsSidecarServer({ env = process.env, probeImpl = requestTarget } = {}) {
  return createServer(async (request, response) => {
    const url = new URL(request.url || "/", "http://hwlab-metrics.local");
    if (request.method === "GET" && (url.pathname === "/health" || url.pathname === "/health/live")) {
      sendJson(response, 200, {
        serviceId: sanitizeLabelValue(env.HWLAB_METRICS_SERVICE_ID || env.HWLAB_SERVICE_ID),
        status: "ok",
        metricsPath: "/metrics"
      });
      return;
    }
    if (request.method === "GET" && url.pathname === "/metrics") {
      const timeoutMs = Number.parseInt(env.HWLAB_METRICS_TARGET_TIMEOUT_MS || "", 10) || DEFAULT_TARGET_TIMEOUT_MS;
      const probe = await probeTarget({ targetUrl: env.HWLAB_METRICS_TARGET_URL, timeoutMs, probeImpl });
      const extraTimeoutMs = Number.parseInt(env.HWLAB_METRICS_EXTRA_TIMEOUT_MS || "", 10) || DEFAULT_TARGET_TIMEOUT_MS;
      const extraMetrics = await probeExtraMetrics({ targetUrl: env.HWLAB_METRICS_EXTRA_URL, timeoutMs: extraTimeoutMs, probeImpl });
      const payload = buildMetricsText({ env, probe, extraMetrics });
      response.writeHead(200, {
        "content-type": "text/plain; version=0.0.4; charset=utf-8",
        "content-length": Buffer.byteLength(payload)
      });
      response.end(payload);
      return;
    }
    sendJson(response, 404, { error: "not_found", path: url.pathname });
  });
}

export function startMetricsSidecar({ env = process.env } = {}) {
  const port = Number.parseInt(env.HWLAB_METRICS_PORT || env.PORT || "", 10) || DEFAULT_PORT;
  const server = createMetricsSidecarServer({ env });
  server.listen(port, "0.0.0.0", () => {
    process.stdout.write(JSON.stringify({ serviceId: env.HWLAB_METRICS_SERVICE_ID || "hwlab-unknown", status: "metrics-listening", port }) + "\n");
  });
  return server;
}

export function isMainModule({ moduleUrl = import.meta.url, argvPath = process.argv[1], realpath = realpathSync } = {}) {
  if (!argvPath) return false;
  if (moduleUrl === pathToFileURL(argvPath).href) return true;
  try {
    return moduleUrl === pathToFileURL(realpath(argvPath)).href;
  } catch {
    return false;
  }
}

if (isMainModule()) {
  startMetricsSidecar();
}
