export const DEFAULT_FILE_TIMEOUT_SECONDS = 60;
export const FILE_TIMEOUT_INTERVAL_SECONDS = 0.25;
export const INTERNAL_FILE_TIMEOUT_SECONDS_ENV = "TESTKIT_INTERNAL_FILE_TIMEOUT_SECONDS";
export const INTERNAL_FILE_DEADLINE_MS_ENV = "TESTKIT_INTERNAL_FILE_DEADLINE_MS";

export function parseFileTimeoutOption(value) {
  return parsePositiveInteger(value, "--file-timeout-seconds");
}

export function normalizeFileTimeoutSeconds(
  value,
  label = "execution.fileTimeoutSeconds"
) {
  return normalizePositiveInteger(value, label);
}

export function normalizeWaitIntervalSeconds(value) {
  if (value == null) return FILE_TIMEOUT_INTERVAL_SECONDS;
  const normalized = Number(value);
  if (!Number.isFinite(normalized) || normalized <= 0) {
    throw new Error("waitFor intervalSeconds must be a positive number.");
  }
  return normalized;
}

export function buildFileTimeoutEnv(fileTimeoutSeconds, startedAtMs) {
  const normalizedTimeoutSeconds = normalizeFileTimeoutSeconds(fileTimeoutSeconds);
  const normalizedStartedAtMs = normalizeTimestamp(startedAtMs, "startedAtMs");

  return {
    [INTERNAL_FILE_TIMEOUT_SECONDS_ENV]: String(normalizedTimeoutSeconds),
    [INTERNAL_FILE_DEADLINE_MS_ENV]: String(
      normalizedStartedAtMs + normalizedTimeoutSeconds * 1000
    ),
  };
}

export function readFileTimeoutBudget(env) {
  if (!env || typeof env !== "object") {
    throw new Error("Runtime file timeout budget is missing.");
  }

  return {
    fileTimeoutSeconds: normalizeFileTimeoutSeconds(
      env[INTERNAL_FILE_TIMEOUT_SECONDS_ENV],
      INTERNAL_FILE_TIMEOUT_SECONDS_ENV
    ),
    deadlineMs: normalizeTimestamp(
      env[INTERNAL_FILE_DEADLINE_MS_ENV],
      INTERNAL_FILE_DEADLINE_MS_ENV
    ),
  };
}

export function remainingFileTimeoutMs(budget, nowMs = Date.now()) {
  const normalizedNowMs = normalizeTimestamp(nowMs, "nowMs");
  const deadlineMs = normalizeTimestamp(budget?.deadlineMs, "deadlineMs");
  return Math.max(0, deadlineMs - normalizedNowMs);
}

export function remainingFileTimeoutSeconds(budget, nowMs = Date.now()) {
  return remainingFileTimeoutMs(budget, nowMs) / 1000;
}

export function formatWaitForTimeoutError(description, fileTimeoutSeconds) {
  const normalizedDescription = String(description || "condition").trim() || "condition";
  const normalizedFileTimeoutSeconds = normalizeFileTimeoutSeconds(
    fileTimeoutSeconds,
    "fileTimeoutSeconds"
  );
  return (
    `Timed out waiting for ${normalizedDescription} before the ` +
    `${normalizedFileTimeoutSeconds}s test file timeout`
  );
}

export function formatFileTimeoutBudgetError(fileTimeoutSeconds) {
  const normalizedFileTimeoutSeconds = normalizeFileTimeoutSeconds(
    fileTimeoutSeconds,
    "fileTimeoutSeconds"
  );
  return `Default runtime exceeded the ${normalizedFileTimeoutSeconds}s test file timeout`;
}

function parsePositiveInteger(value, label) {
  const parsed = Number.parseInt(String(value), 10);
  if (!Number.isInteger(parsed) || parsed <= 0) {
    throw new Error(`Invalid ${label} value "${value}". Expected a positive integer.`);
  }
  return parsed;
}

function normalizePositiveInteger(value, label) {
  const parsed = Number(value);
  if (!Number.isInteger(parsed) || parsed <= 0) {
    throw new Error(`${label} must be a positive integer.`);
  }
  return parsed;
}

function normalizeTimestamp(value, label) {
  const parsed = Number(value);
  if (!Number.isInteger(parsed) || parsed <= 0) {
    throw new Error(`${label} must be a positive integer timestamp.`);
  }
  return parsed;
}
