export type ContentRecord = Record<string, unknown>;

const RESERVED_PREFIX = "__";

export function isReservedContentKey(key: string): boolean {
  return key.startsWith(RESERVED_PREFIX);
}

export function isPlainObject(value: unknown): value is ContentRecord {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}

export function humanizeFieldLabel(key: string): string {
  return key
    .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
    .replace(/[_-]+/g, " ")
    .replace(/\s+/g, " ")
    .trim()
    .replace(/^./, (c) => c.toUpperCase());
}

export function isLongTextField(key: string, value: string): boolean {
  if (/subtitle|description|desc|body|promise|text|message|content|summary/i.test(key)) {
    return true;
  }
  return value.length > 100;
}

export function isUrlLikeField(key: string): boolean {
  return /src|href|url|image|logo|avatar|thumbnail/i.test(key);
}

export function looksLikeImageUrl(value: string): boolean {
  const trimmed = value.trim();
  if (!trimmed) return false;
  return (
    /^https?:\/\//i.test(trimmed) ||
    /^data:image\//i.test(trimmed) ||
    trimmed.startsWith("/")
  );
}

const EMOJI_FIELD_KEYS = /^(emoji|headingIcon|introIcon|otherSubjectsIcon|thisAddsIcon)$/i;

/** Fields that store a single emoji character (not an image URL). */
export function isEmojiField(key: string, value?: unknown): boolean {
  if (EMOJI_FIELD_KEYS.test(key)) return true;
  if (/^icon$/i.test(key)) {
    const s = typeof value === "string" ? value : "";
    return !looksLikeImageUrl(s);
  }
  return false;
}

/** Fields that store an image or icon URL (including uploaded data URLs). */
export function isImageIconField(key: string, value?: unknown): boolean {
  if (/^(iconSrc|imageUrl|logoUrl|avatar|thumbnail)$/i.test(key)) return true;
  if (/^icon$/i.test(key) && looksLikeImageUrl(String(value ?? ""))) return true;
  return false;
}

export function pathToString(path: Array<string | number>): string {
  return path
    .map((segment, index) => {
      if (typeof segment === "number") {
        return `[${segment}]`;
      }
      return index === 0 ? segment : `.${segment}`;
    })
    .join("");
}

export function getValueAtPath(root: unknown, path: Array<string | number>): unknown {
  let current: unknown = root;
  for (const segment of path) {
    if (current === null || current === undefined) return undefined;
    if (typeof segment === "number") {
      if (!Array.isArray(current)) return undefined;
      current = current[segment];
      continue;
    }
    if (!isPlainObject(current)) return undefined;
    current = current[segment];
  }
  return current;
}

export function setValueAtPath(root: ContentRecord, path: Array<string | number>, value: unknown): ContentRecord {
  const cloned = JSON.parse(JSON.stringify(root)) as ContentRecord;
  if (path.length === 0) {
    return isPlainObject(value) ? (value as ContentRecord) : cloned;
  }

  let current: ContentRecord | unknown[] = cloned;
  for (let i = 0; i < path.length - 1; i++) {
    const segment = path[i];
    const nextSegment = path[i + 1];
    if (typeof segment === "number") {
      if (!Array.isArray(current)) return cloned;
      if (current[segment] === undefined) {
        current[segment] = typeof nextSegment === "number" ? [] : {};
      }
      current = current[segment] as ContentRecord | unknown[];
      continue;
    }
    if (!isPlainObject(current)) return cloned;
    if (current[segment] === undefined) {
      current[segment] = typeof nextSegment === "number" ? [] : {};
    }
    current = current[segment] as ContentRecord | unknown[];
  }

  const last = path[path.length - 1];
  if (typeof last === "number") {
    if (!Array.isArray(current)) return cloned;
    current[last] = value;
  } else if (isPlainObject(current)) {
    current[last] = value;
  }

  return cloned;
}

export function createEmptyArrayItem(sample: unknown): unknown {
  if (typeof sample === "string") return "";
  if (typeof sample === "number") return 0;
  if (typeof sample === "boolean") return false;
  if (Array.isArray(sample)) return [];
  if (isPlainObject(sample)) {
    return Object.fromEntries(Object.entries(sample).map(([key, value]) => [key, createEmptyArrayItem(value)]));
  }
  return "";
}

export function getFieldLabel(
  key: string,
  fieldLabels?: Record<string, string> | null
): string {
  return fieldLabels?.[key] ?? humanizeFieldLabel(key);
}

export function isBlankContentValue(value: unknown): boolean {
  if (value === null || value === undefined) return true;
  if (typeof value === "string") return value.trim() === "";
  if (typeof value === "number" || typeof value === "boolean") return false;
  if (Array.isArray(value)) return value.length === 0 || value.every(isBlankContentValue);
  if (isPlainObject(value)) return Object.values(value).every(isBlankContentValue);
  return false;
}

/** Strip placeholder-only repeater rows (e.g. filters: [{}]) from editor content. */
export function normalizeOptionalObjectArrays(content: ContentRecord): ContentRecord {
  const cloned = JSON.parse(JSON.stringify(content)) as ContentRecord;

  const walk = (node: unknown): void => {
    if (!isPlainObject(node)) return;

    for (const [key, value] of Object.entries(node)) {
      if (key === "filters" && Array.isArray(value)) {
        node[key] = value.filter((item) => !isBlankContentValue(item));
        continue;
      }

      if (Array.isArray(value)) {
        for (const item of value) walk(item);
      } else if (isPlainObject(value)) {
        walk(value);
      }
    }
  };

  walk(cloned);
  return cloned;
}

export function validateRequiredContent(content: ContentRecord): string[] {
  const required = Array.isArray(content.__required)
    ? content.__required.filter((item): item is string => typeof item === "string" && item.trim() !== "")
    : [];

  const errors: string[] = [];
  for (const fieldPath of required) {
    const value = getValueAtPath(content, fieldPath.split("."));
    if (value === undefined || value === null) {
      errors.push(`${humanizeFieldLabel(fieldPath)} is required.`);
      continue;
    }
    if (typeof value === "string" && !value.trim()) {
      errors.push(`${humanizeFieldLabel(fieldPath)} is required.`);
    }
  }
  return errors;
}
