/**
 * Shared utility functions for Salesforce Pub/Sub subscribers
 *
 * Provides common payload extraction and validation logic used by both
 * CDC and Platform Event subscribers.
 */

import type { ChangeEventHeader } from "./pubsub.types.js";

/**
 * Check if a callback type represents actual event data
 */
export function isDataCallback(callbackType: string): boolean {
  const normalized = String(callbackType || "").toLowerCase();
  return normalized === "data" || normalized === "event";
}

/**
 * Extract payload from Pub/Sub event data
 */
export function extractPayload(data: unknown): Record<string, unknown> | undefined {
  if (!data || typeof data !== "object") {
    return undefined;
  }

  const candidate = data as { payload?: unknown };
  if (candidate.payload && typeof candidate.payload === "object") {
    return candidate.payload as Record<string, unknown>;
  }

  return data as Record<string, unknown>;
}

/**
 * Extract a string field from payload, trying multiple possible field names
 */
export function extractStringField(
  payload: Record<string, unknown> | undefined,
  fieldNames: string[]
): string | undefined {
  if (!payload) return undefined;

  for (const field of fieldNames) {
    const value = payload[field];
    if (typeof value === "string") {
      const trimmed = value.trim();
      if (trimmed.length > 0) {
        return trimmed;
      }
    }
  }
  return undefined;
}

/**
 * Extract ChangeEventHeader from CDC payload
 */
export function extractChangeEventHeader(
  payload: Record<string, unknown>
): ChangeEventHeader | undefined {
  const header = payload["ChangeEventHeader"];
  if (header && typeof header === "object") {
    return header as ChangeEventHeader;
  }
  return undefined;
}

/**
 * Extract record IDs from CDC payload
 */
export function extractRecordIds(payload: Record<string, unknown> | undefined): string[] {
  if (!payload) {
    return [];
  }

  const header = extractChangeEventHeader(payload);
  const ids = header?.recordIds ?? [];

  if (Array.isArray(ids)) {
    return ids.filter((id): id is string => typeof id === "string" && id.trim().length > 0);
  }

  return [];
}

/**
 * Extract changed field names from CDC payload
 */
export function extractChangedFields(payload: Record<string, unknown> | undefined): Set<string> {
  if (!payload) return new Set();

  const header = extractChangeEventHeader(payload);
  const headerChangedFields = Array.isArray(header?.changedFields)
    ? (header?.changedFields as unknown[]).filter(
        (field): field is string => typeof field === "string" && field.length > 0
      )
    : [];

  // CDC provides changed fields in different formats depending on API version
  const changedFieldsArray =
    (payload["changedFields"] as string[] | undefined) ||
    (payload["changeOrigin"] as { changedFields?: string[] } | undefined)?.changedFields ||
    [];

  return new Set([
    ...headerChangedFields,
    ...changedFieldsArray.filter(field => typeof field === "string" && field.length > 0),
  ]);
}

/**
 * Parse numRequested config value with validation
 */
export function parseNumRequested(
  rawValue: string | undefined,
  defaultValue = 25,
  onWarn?: (message: string, context: Record<string, unknown>) => void
): number {
  const raw = rawValue ?? String(defaultValue);
  const parsed = Number.parseInt(raw, 10);

  if (!Number.isFinite(parsed) || parsed <= 0) {
    onWarn?.("Invalid SF_PUBSUB_NUM_REQUESTED value; using default", {
      rawValue: raw,
      defaultValue,
    });
    return defaultValue;
  }

  return parsed;
}
