import { buildKnownFailureIssueValidationSummaryLines } from "../known-failures/github.mjs";

export function formatDuration(durationMs) {
  const totalSeconds = Math.max(0, Math.round(durationMs / 1000));
  const minutes = Math.floor(totalSeconds / 60);
  const seconds = totalSeconds % 60;
  if (minutes === 0) return `${seconds}s`;
  return `${minutes}m ${String(seconds).padStart(2, "0")}s`;
}

export function formatServiceSummary(result) {
  const skippedSuites = result.skippedSuiteCount || 0;
  const passedSuites = result.completedSuiteCount - result.failedSuiteCount - skippedSuites;
  const notRunSuites = result.suiteCount - result.completedSuiteCount;
  let detail = `${passedSuites}/${result.suiteCount} suites passed`;
  if ((result.totalFileCount || 0) > 0) {
    detail += `, ${result.passedFileCount}/${result.totalFileCount} files passed`;
  }
  if (skippedSuites > 0) {
    detail += `, ${skippedSuites} ${pluralize(skippedSuites, "suite", "suites")} skipped`;
  } else if ((result.skippedFileCount || 0) > 0) {
    detail += `, ${result.skippedFileCount} ${pluralize(result.skippedFileCount, "file", "files")} skipped`;
  }
  if (notRunSuites > 0) {
    detail += `, ${notRunSuites} ${pluralize(notRunSuites, "suite", "suites")} not run`;
  } else if ((result.notRunFileCount || 0) > 0) {
    detail += `, ${result.notRunFileCount} ${pluralize(result.notRunFileCount, "file", "files")} not run`;
  }
  return detail;
}

export function formatError(error) {
  if (error instanceof Error) return sanitizeErrorMessage(error.message);
  return sanitizeErrorMessage(String(error));
}

export function longestServiceName(results) {
  return results.reduce((max, result) => Math.max(max, result.name.length), 4);
}

export function formatFrameworkLabel(framework) {
  if (!framework || framework === "k6") return "";
  return framework;
}

export function formatSuiteFramework(framework) {
  const label = formatFrameworkLabel(framework);
  return label ? ` [${label}]` : "";
}

export function buildRunSummaryLines(results, durationMs, knownFailureIssueValidation = null) {
  return buildCompactRunSummaryLines(results, durationMs, knownFailureIssueValidation);
}

export function buildCompactRunSummaryLines(
  results,
  durationMs,
  knownFailureIssueValidation = null
) {
  const totals = summarizeResults(results);
  const lines = [
    "",
    `Summary: ${totals.passedFiles} passed, ${totals.failedFiles} failed, ${totals.skippedFiles} skipped, ${totals.notRunFiles} not run across ${totals.totalFiles} ${pluralize(totals.totalFiles, "file", "files")} in ${formatDuration(durationMs)}`,
  ];

  const failures = collectFailedFiles(results);
  if (failures.length > 0) {
    lines.push("", "Failures:");
    for (const failure of failures) {
      lines.push(`  ${failure.file.path}`);
      lines.push(`    ${failure.primaryMessage}`);
      for (const detail of failure.extraLines.slice(0, 3)) {
        lines.push(`    ${detail}`);
      }
    }
  }

  const serviceErrors = collectServiceErrors(results);
  if (serviceErrors.length > 0) {
    lines.push("", "Runtime Errors:");
    for (const item of serviceErrors) {
      lines.push(`  ${item.service}`);
      lines.push(`    ${item.message}`);
    }
  }

  const knownFailureIssueLines = buildKnownFailureIssueValidationSummaryLines(
    knownFailureIssueValidation
  );
  if (knownFailureIssueLines.length > 0) {
    lines.push(...knownFailureIssueLines);
  }

  lines.push("");
  lines.push(
    totals.failedServices > 0 ? `Result: FAILED (${totals.failedServices}/${totals.totalServices} services failed)` : "Result: PASSED"
  );
  return lines;
}

export function buildDebugRunSummaryLines(results, durationMs, knownFailureIssueValidation = null) {
  const totalServices = results.length;
  const executedServices = results.filter((result) => !result.skipped);
  const skippedServices = results.filter((result) => result.skipped);
  const failedServices = executedServices.filter((result) => result.failed);
  const passedServices = executedServices.filter((result) => !result.failed);
  const totalSuites = executedServices.reduce((sum, result) => sum + result.suiteCount, 0);
  const completedSuites = executedServices.reduce(
    (sum, result) => sum + result.completedSuiteCount,
    0
  );
  const skippedSuites = executedServices.reduce(
    (sum, result) => sum + (result.skippedSuiteCount || 0),
    0
  );
  const failedSuites = executedServices.reduce((sum, result) => sum + result.failedSuiteCount, 0);
  const passedSuites = completedSuites - failedSuites - skippedSuites;
  const totalFiles = executedServices.reduce((sum, result) => sum + (result.totalFileCount || 0), 0);
  const passedFiles = executedServices.reduce((sum, result) => sum + (result.passedFileCount || 0), 0);
  const skippedFiles = executedServices.reduce(
    (sum, result) => sum + (result.skippedFileCount || 0),
    0
  );
  const lines = [
    "",
    "══ Summary ══",
    [
      `services ${passedServices.length}/${executedServices.length} passed`,
      `suites ${passedSuites}/${totalSuites} passed`,
      totalFiles > 0 ? `files ${passedFiles}/${totalFiles} passed` : null,
      skippedSuites > 0 ? `suites ${skippedSuites} skipped` : null,
      skippedFiles > 0 ? `files ${skippedFiles} skipped` : null,
      skippedServices.length > 0 ? `${skippedServices.length} skipped` : null,
      `duration ${formatDuration(durationMs)}`,
    ]
      .filter(Boolean)
      .join("  ·  "),
  ];

  for (const result of results) {
    const status = isServiceEffectivelySkipped(result)
      ? "SKIP"
      : result.failed
        ? "FAIL"
        : "PASS";
    const detail = result.skipped
      ? "no matching suites"
      : formatServiceSummary(result);
    lines.push(
      `${status.padEnd(4)} ${result.name.padEnd(longestServiceName(results))}  ${detail}  ·  ${formatDuration(result.durationMs)}`
    );

    if (!result.failed) continue;

    for (const suite of result.suites.filter((entry) => entry.failed)) {
      const fileDetail =
        suite.failedFiles.length > 0 ? `  (${suite.failedFiles.join(", ")})` : "";
      lines.push(
        `  - ${suite.type}:${suite.name}${formatSuiteFramework(suite.framework)}${fileDetail} · ${formatDuration(suite.durationMs)}`
      );
      if (suite.error) {
        lines.push(`    ${suite.error}`);
      }
    }
    for (const error of result.errors) {
      lines.push(`  - worker error: ${error}`);
    }
  }

  const knownFailureIssueLines = buildKnownFailureIssueValidationSummaryLines(
    knownFailureIssueValidation
  );
  if (knownFailureIssueLines.length > 0) {
    lines.push(...knownFailureIssueLines);
  }

  if (failedServices.length > 0) {
    lines.push("", `Result: FAILED (${failedServices.length}/${totalServices} services failed)`);
    return lines;
  }

  lines.push("", "Result: PASSED");
  return lines;
}

function sanitizeErrorMessage(message) {
  return message
    .replace(/Command failed with exit code (\d+): .*?[\\/]vendor[\\/]k6 run\b/g, "Default runtime failed with exit code $1:")
    .replace(/Command failed with exit code (\d+): k6 run\b/g, "Default runtime failed with exit code $1:")
    .replace(/[\\/]vendor[\\/]k6\b/g, "default-runtime");
}

function summarizeResults(results) {
  const executedServices = results.filter((result) => !result.skipped);
  return {
    totalServices: results.length,
    failedServices: executedServices.filter((result) => result.failed).length,
    totalFiles: executedServices.reduce((sum, result) => sum + (result.totalFileCount || 0), 0),
    passedFiles: executedServices.reduce((sum, result) => sum + (result.passedFileCount || 0), 0),
    failedFiles: executedServices.reduce((sum, result) => sum + (result.failedFileCount || 0), 0),
    skippedFiles: executedServices.reduce((sum, result) => sum + (result.skippedFileCount || 0), 0),
    notRunFiles: executedServices.reduce((sum, result) => sum + (result.notRunFileCount || 0), 0),
  };
}

function collectFailedFiles(results) {
  const failures = [];
  for (const result of results) {
    for (const suite of result.suites || []) {
      for (const file of suite.files || []) {
        if (file.status !== "failed") continue;
        const rankedDetails = rankFailureDetails(file.failureDetails || []);
        const primaryDetail = rankedDetails[0] || null;
        const fallbackMessages = rankedDetails
          .map((detail) => detail.message || detail.title)
          .filter(Boolean)
          .map((message) => sanitizeErrorMessage(String(message).trim()));
        const extraLines = [];
        if (primaryDetail) {
          const responseLine = formatFailureResponsePreview(primaryDetail);
          if (responseLine) extraLines.push(responseLine);
          const triageLine = formatTriageLine(file.triage || null);
          if (triageLine) extraLines.push(triageLine);
          const requestLine = formatFailureRequestHint(primaryDetail);
          if (requestLine) extraLines.push(requestLine);
        } else {
          const triageLine = formatTriageLine(file.triage || null);
          if (triageLine) extraLines.push(triageLine);
        }

        for (const detail of rankedDetails.slice(primaryDetail ? 1 : 0)) {
          const line = sanitizeErrorMessage(String(detail.message || detail.title || "").trim());
          if (line && !extraLines.includes(line)) extraLines.push(line);
        }
        failures.push({
          file,
          primaryMessage: resolvePrimaryFailureMessage(file, suite, primaryDetail, fallbackMessages),
          extraLines,
        });
      }
    }
  }
  return failures.sort((left, right) => left.file.path.localeCompare(right.file.path));
}

function resolvePrimaryFailureMessage(file, suite, primaryDetail, fallbackMessages) {
  if (primaryDetail?.message) {
    return sanitizeErrorMessage(String(primaryDetail.message).trim());
  }

  const fileError = sanitizeErrorMessage(file.error || "");
  if (fileError && !isThresholdWrapperMessage(fileError)) {
    return fileError;
  }

  if (fallbackMessages.length > 0) {
    return fallbackMessages[0];
  }

  if (fileError) {
    return fileError;
  }

  return sanitizeErrorMessage(suite.error || "Failed");
}

function rankFailureDetails(details) {
  return [...(Array.isArray(details) ? details : [])].sort((left, right) => {
    return failureDetailRank(left) - failureDetailRank(right) || String(left?.key || "").localeCompare(String(right?.key || ""));
  });
}

function failureDetailRank(detail) {
  if (detail?.kind === "http-assertion") return 1;
  if (detail?.request && detail?.response) return 2;
  if (detail?.location || detail?.stack) return 3;
  if (detail?.message) return 4;
  return 5;
}

function formatFailureResponsePreview(detail) {
  const bodyPreview = detail?.response?.bodyPreview;
  if (!bodyPreview) return null;
  return `response: ${sanitizeInline(bodyPreview, 220)}`;
}

function formatFailureRequestHint(detail) {
  const method = detail?.request?.method;
  const path = detail?.request?.path;
  if (!method || !path) return null;
  const requestId = detail?.request?.requestId;
  if (requestId) {
    return `logs: requestId=${requestId}`;
  }
  return `request: ${method} ${path}`;
}

function formatTriageLine(triage) {
  if (!triage) return null;
  if (triage.status === "untriaged") return "triage: untriaged";

  const entry = triage.entries?.[0];
  if (!entry?.issue) {
    if (triage.status === "validation_unavailable") {
      const reason = triage.availability?.reason ? ` (${triage.availability.reason})` : "";
      return `triage: validation unavailable${reason}`;
    }
    return null;
  }

  const issueLabel = `#${entry.issue.number}`;
  const validationStatus = entry.validationStatus || null;
  if (validationStatus === "closed_but_failing") {
    return `triage: known issue ${issueLabel} closed but still failing`;
  }
  if (validationStatus === "validation_unavailable") {
    const reason = triage.availability?.mode === "cache" ? "cache" : "validation unavailable";
    return `triage: known issue ${issueLabel} (${reason})`;
  }
  if (entry.github?.state === "open" || entry.state === "open") {
    return `triage: known issue ${issueLabel} open`;
  }
  if (entry.github?.state === "closed" || entry.state === "closed") {
    return `triage: known issue ${issueLabel} closed`;
  }
  return `triage: known issue ${issueLabel}`;
}

function isThresholdWrapperMessage(message) {
  return /Default runtime thresholds failed:/.test(String(message || ""));
}

function sanitizeInline(message, maxLength = 180) {
  return String(message || "").replace(/\s+/g, " ").trim().slice(0, maxLength);
}

function collectServiceErrors(results) {
  const items = [];
  for (const result of results) {
    for (const error of result.errors || []) {
      items.push({
        service: result.name,
        message: sanitizeErrorMessage(error),
      });
    }
  }
  return items;
}

function pluralize(value, singular, plural) {
  return value === 1 ? singular : plural;
}

function isServiceEffectivelySkipped(result) {
  if (result.skipped) return true;
  return (
    !result.failed &&
    (result.skippedSuiteCount || 0) > 0 &&
    (result.skippedSuiteCount || 0) === result.suiteCount &&
    (result.notRunFileCount || 0) === 0
  );
}
