import path from "path";
import { fileURLToPath } from "url";

export function determineDefaultRuntimeFailure(result, summary, firstLine) {
  const fatalRuntimeError = extractDefaultRuntimeFatalError(result.stderr || "", firstLine);
  if (fatalRuntimeError) {
    return `Default runtime uncaught error: ${fatalRuntimeError}`;
  }

  const failedThresholds = extractDefaultRuntimeThresholdFailures(summary);
  if (failedThresholds.length > 0) {
    return `Default runtime thresholds failed: ${failedThresholds.join(", ")}`;
  }

  if (result.exitCode !== 0) {
    return sanitizeDefaultRuntimeExitError(
      result.exitCode,
      result.stderr || result.stdout || "",
      firstLine
    );
  }

  return null;
}

export function extractDefaultRuntimeFatalError(stderr, firstLine) {
  if (!stderr || !/source=stacktrace/.test(stderr)) return null;
  const matched = stderr.match(/Error:\s([^\n]+)/);
  return matched?.[1]?.trim() || firstLine(stderr);
}

export function extractDefaultRuntimeFatalDetail(stderr, firstLine) {
  if (!stderr || !/source=stacktrace/.test(stderr)) return null;
  const message = extractDefaultRuntimeFatalError(stderr, firstLine);
  if (!message) return null;

  const location = extractFirstLocation(stderr);
  return {
    kind: "runtime-exception",
    key: location ? `${location.path}:${location.line}:${location.column}` : message,
    title: "Uncaught runtime exception",
    message: `Uncaught testkit suite error: ${message}`,
    location,
    stack: sanitizeStack(stderr),
  };
}

export function extractDefaultRuntimeThresholdFailures(summary) {
  const metrics = summary?.metrics;
  if (!metrics || typeof metrics !== "object") return [];

  const failures = [];
  for (const [metricName, metricSummary] of Object.entries(metrics)) {
    const thresholds = metricSummary?.thresholds;
    if (!thresholds || typeof thresholds !== "object") continue;
    for (const [threshold, crossed] of Object.entries(thresholds)) {
      if (crossed === true) {
        failures.push(`${metricName}(${threshold})`);
      }
    }
  }

  return failures.sort();
}

export function sanitizeDefaultRuntimeExitError(exitCode, output, firstLine) {
  const message = firstLine(output);
  if (message) {
    return `Default runtime failed with exit code ${exitCode}: ${message}`;
  }
  return `Default runtime failed with exit code ${exitCode}`;
}

function extractFirstLocation(stderr) {
  const locations = [...String(stderr).matchAll(/(file:\/\/[^\s)]+|\/[^\s):]+):(\d+):(\d+)/g)].map((match) => ({
    path: normalizeLocationPath(match[1]),
    line: Number(match[2]),
    column: Number(match[3]),
  }));
  if (locations.length === 0) return null;

  return (
    locations.find((location) => isPreferredLocation(location.path)) ||
    locations.find((location) => !isRuntimeInternalLocation(location.path)) ||
    locations[0]
  );
}

function normalizeLocationPath(rawPath) {
  if (!rawPath) return rawPath;
  if (rawPath.startsWith("file://")) {
    try {
      return fileURLToPath(rawPath);
    } catch {
      return rawPath;
    }
  }
  return rawPath;
}

function sanitizeStack(stderr) {
  return String(stderr)
    .split(/\r?\n/)
    .map((line) => line.trim())
    .filter(Boolean)
    .join("\n");
}

function isPreferredLocation(filePath) {
  if (typeof filePath !== "string") return false;
  const normalized = filePath.split(path.sep).join("/");
  return normalized.includes("/.testkit/_bundles/") || normalized.includes("/__testkit__/");
}

function isRuntimeInternalLocation(filePath) {
  if (typeof filePath !== "string") return false;
  const normalized = filePath.split(path.sep).join("/");
  return normalized.includes("/testkit/lib/runtime-src/k6/") || normalized.includes("/vendor/k6");
}
