import { createHash } from "node:crypto";
import type { JsonValue } from "../../shared/src/index";

const defaultStringLimit = 8_000;
const bodyInlineLimit = 16_384;
const bodyPreviewLimit = 2_000;
const maxArrayItems = 100;
const maxObjectKeys = 100;
const maxDepth = 8;

function hashText(value: string): string {
  return createHash("sha256").update(value).digest("hex");
}

function boundedString(value: string, limit: number): JsonValue {
  if (value.length <= limit) return value;
  return {
    omitted: true,
    omittedReason: "string too large for database inline JSON storage",
    chars: value.length,
    preview: value.slice(0, limit),
    sha256: hashText(value),
  };
}

function compactBodyText(value: string): JsonValue {
  if (value.length <= bodyInlineLimit) return value;
  return {
    omitted: true,
    omittedReason: "microservice HTTP body is stored as metadata only",
    bodyPreview: value.slice(0, bodyPreviewLimit),
    bodyTextChars: value.length,
    bodyTextSha256: hashText(value),
  };
}

export function compactJsonForStorage(value: unknown, depth = 0, key = ""): JsonValue {
  if (value === null || typeof value === "number" || typeof value === "boolean") return value;
  if (typeof value === "string") return key === "bodyText" ? compactBodyText(value) : boundedString(value, defaultStringLimit);
  if (Array.isArray(value)) {
    const items = value.slice(0, maxArrayItems).map((item) => compactJsonForStorage(item, depth + 1));
    if (value.length > maxArrayItems) items.push({ truncatedItems: value.length - maxArrayItems });
    return items;
  }
  if (typeof value === "object" && value !== null) {
    if (depth >= maxDepth) return "<truncated:depth>";
    const result: Record<string, JsonValue> = {};
    const entries = Object.entries(value as Record<string, unknown>);
    for (const [childKey, item] of entries.slice(0, maxObjectKeys)) {
      if (childKey === "bodyText" && typeof item === "string") {
        Object.assign(result, compactBodyText(item));
        continue;
      }
      result[childKey] = compactJsonForStorage(item, depth + 1, childKey);
    }
    if (entries.length > maxObjectKeys) result.truncatedKeys = entries.length - maxObjectKeys;
    return result;
  }
  return String(value);
}
