// Shared template-rendering core used by the renderNotificationTemplate query
// and the dispatchNotification engine, so both paths apply one interpolation /
// validation / escaping semantics (null counts as missing, `date` type
// accepted, identical placeholder grammar, htmlBody always HTML-escaped).

import type { VariableSchemaShape } from "../command/createNotificationTemplate";

const PLACEHOLDER_PATTERN = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;

export function htmlEscape(value: string): string {
  return value
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#39;");
}

function stringifyValue(value: unknown): string {
  if (value === null || value === undefined) return "";
  if (typeof value === "string") return value;
  if (typeof value === "number" || typeof value === "boolean") return String(value);
  return JSON.stringify(value);
}

function isValueOfType(value: unknown, type: string): boolean {
  switch (type) {
    case "string":
      return typeof value === "string";
    case "number":
      return typeof value === "number";
    case "boolean":
      return typeof value === "boolean";
    case "date":
      return typeof value === "string" || value instanceof Date || typeof value === "number";
    default:
      // Unknown declared type — accept any non-null value.
      return value !== null && value !== undefined;
  }
}

/** Returns a human-readable error, or null when the payload satisfies the schema. */
export function validatePayloadVars(
  schema: VariableSchemaShape,
  payload: Record<string, unknown>,
): string | null {
  for (const variable of schema.variables) {
    const supplied = payload[variable.name];
    const isMissing = supplied === undefined || supplied === null;
    if (variable.required && isMissing) {
      return `missing required variable: ${variable.name}`;
    }
    if (!isMissing && !isValueOfType(supplied, variable.type)) {
      return `wrong type for variable: ${variable.name}`;
    }
  }
  return null;
}

export function interpolate(
  template: string,
  payload: Record<string, unknown>,
  escape: boolean,
): string {
  return template.replace(PLACEHOLDER_PATTERN, (_, name: string) => {
    const raw = stringifyValue(payload[name]);
    return escape ? htmlEscape(raw) : raw;
  });
}
