export const HWLAB_ERROR_DIAGNOSTIC_VERSION = "hwlab-error-diagnostic-v1";

export function decorateErrorPayloadForHttp(payload, options = {}) {
  const statusCode = numericStatus(options.statusCode);
  if (!statusCode || statusCode < 400 || !plainObject(payload) || !("error" in payload)) return payload;

  const context = options.context ?? options.response?.hwlabHttpRequestContext ?? options.request?.hwlabHttpRequestContext ?? {};
  const originalError = normalizeErrorObject(payload.error, payload);
  if (!originalError) return payload;

  const rawCode = originalError.code ?? payload.code ?? payload.error ?? statusCodeToCode(statusCode);
  const diagnosticCode = safeCode(rawCode);
  const errorCode = typeof originalError.code === "number" ? originalError.code : diagnosticCode;
  const message = firstText(originalError.message, payload.message, diagnosticCode);
  const userMessage = firstText(originalError.userMessage, payload.userMessage, message);
  const diagnostic = {
    contractVersion: HWLAB_ERROR_DIAGNOSTIC_VERSION,
    traceId: safeOtelTraceId(context.traceId) ?? safeOtelTraceId(originalError.diagnostic?.traceId) ?? safeOtelTraceId(originalError.otelTraceId) ?? safeOtelTraceId(payload.otelTraceId) ?? null,
    requestId: safeRequestId(context.requestId) ?? safeRequestId(originalError.diagnostic?.requestId) ?? safeRequestId(originalError.requestId) ?? safeRequestId(payload.requestId) ?? null,
    serviceId: safeToken(originalError.serviceId ?? payload.serviceId ?? options.serviceId, "hwlab-cloud-api"),
    route: safeRoute(originalError.route ?? payload.route ?? options.route ?? context.route),
    layer: safeToken(originalError.layer ?? options.layer, "api"),
    category: safeToken(originalError.category ?? options.category, categoryForStatus(statusCode)),
    code: diagnosticCode,
    httpStatus: statusCode,
    source: safeToken(options.source, "server"),
    observedAt: new Date().toISOString(),
    valuesPrinted: false
  };
  const nextError = {
    ...originalError,
    code: errorCode,
    message,
    userMessage,
    diagnostic: {
      ...(plainObject(originalError.diagnostic) ? originalError.diagnostic : {}),
      ...diagnostic
    },
    valuesPrinted: originalError.valuesPrinted === true ? true : false
  };
  const httpContext = options.context ?? options.response?.hwlabHttpRequestContext ?? options.request?.hwlabHttpRequestContext;
  if (plainObject(httpContext)) {
    httpContext.lastHttpError = { code: diagnosticCode, category: diagnostic.category, layer: diagnostic.layer, httpStatus: statusCode, valuesPrinted: false };
  }
  return { ...payload, error: nextError };
}

export function logErrorEnvelope(payload, options = {}) {
  const statusCode = numericStatus(options.statusCode);
  if (!statusCode || statusCode < 400 || !plainObject(payload) || !plainObject(payload.error)) return;
  const diagnostic = plainObject(payload.error.diagnostic) ? payload.error.diagnostic : null;
  if (!diagnostic) return;
  const record = {
    event: "hwlab.error",
    statusCode,
    trace_id: diagnostic.traceId ?? null,
    request_id: diagnostic.requestId ?? null,
    route: diagnostic.route ?? null,
    serviceId: diagnostic.serviceId ?? "hwlab-cloud-api",
    error: {
      code: payload.error.code ?? diagnostic.code ?? "unknown_error",
      layer: payload.error.layer ?? diagnostic.layer ?? "api",
      category: payload.error.category ?? diagnostic.category ?? categoryForStatus(statusCode),
      retryable: payload.error.retryable ?? null
    },
    valuesPrinted: false
  };
  const logger = options.logger ?? console;
  const line = JSON.stringify(record);
  if (statusCode >= 500 && typeof logger.error === "function") {
    logger.error(line);
  } else if (typeof logger.warn === "function") {
    logger.warn(line);
  }
}

function normalizeErrorObject(value, payload) {
  if (plainObject(value)) return { ...value };
  const text = firstText(value, payload?.message, "unknown_error");
  return {
    code: safeCode(text),
    message: text,
    userMessage: text
  };
}

function numericStatus(value) {
  const status = Number(value);
  return Number.isInteger(status) && status >= 100 && status <= 599 ? status : null;
}

function statusCodeToCode(statusCode) {
  if (statusCode === 400) return "bad_request";
  if (statusCode === 401) return "auth_required";
  if (statusCode === 403) return "forbidden";
  if (statusCode === 404) return "not_found";
  if (statusCode === 409) return "conflict";
  if (statusCode === 429) return "rate_limited";
  if (statusCode >= 500) return "internal_error";
  return "error";
}

function categoryForStatus(statusCode) {
  if (statusCode >= 500) return "server";
  if (statusCode >= 400) return "client";
  return "error";
}

function firstText(...values) {
  for (const value of values) {
    const text = String(value ?? "").trim();
    if (text) return text;
  }
  return "unknown_error";
}

function safeCode(value) {
  const text = firstText(value).toLowerCase().replace(/[^a-z0-9_.:-]+/gu, "_").replace(/^_+|_+$/gu, "");
  return text.slice(0, 96) || "unknown_error";
}

function safeToken(value, fallback) {
  const text = String(value ?? "").trim();
  return /^[A-Za-z0-9_.:-]{1,96}$/u.test(text) ? text : fallback;
}

function safeRoute(value) {
  const text = String(value ?? "").trim().split("?")[0] || "/";
  if (!text.startsWith("/")) return "/";
  return text.slice(0, 180);
}

function safeRequestId(value) {
  const text = String(value ?? "").trim();
  return /^[A-Za-z0-9_.:-]{3,180}$/u.test(text) ? text : null;
}

function safeOtelTraceId(value) {
  const text = String(value ?? "").trim().toLowerCase();
  return /^[0-9a-f]{32}$/u.test(text) && text !== "00000000000000000000000000000000" ? text : null;
}

function plainObject(value) {
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
}
