import { parseCertificateFieldTextParts } from 'src/pages/dashboard/Learning/Settings/Certificates/Create/utils';
import { type TCustomFunction } from 'src/utils/i18n';

export const TOKEN_ATTR = 'data-token-text';

const tokenLabel = (token: string): string =>
  token.replace(/^\{/, '').replace(/\}$/, '');

/** Serializes the editor DOM back to the plain-text value (pills -> `{token}`). */
const serialize = (root: Node): string => {
  let out = '';
  root.childNodes.forEach(child => {
    if (child.nodeType === Node.TEXT_NODE) {
      out += child.textContent ?? '';
    } else if (child instanceof HTMLElement) {
      const token = child.getAttribute(TOKEN_ATTR);
      if (token !== null) out += token;
      else if (child.tagName === 'BR') out += '\n';
      else out += serialize(child);
    }
  });
  return out;
};

export const readEditorValue = (el: HTMLElement): string => {
  const empty = el.textContent === '' && !el.querySelector(`[${TOKEN_ATTR}]`);
  return empty ? '' : serialize(el);
};

/**
 * Builds a token pill node. Visual styling lives in the editor stylesheet
 * (keyed off `TOKEN_ATTR`) so pills stay theme-aware without being rebuilt.
 */
export const createPillElement = (token: string): HTMLSpanElement => {
  const pill = document.createElement('span');
  pill.setAttribute(TOKEN_ATTR, token);
  pill.setAttribute('contenteditable', 'false');
  pill.textContent = tokenLabel(token);
  return pill;
};

export const buildEditorDom = (
  el: HTMLElement,
  value: string,
  translate: TCustomFunction,
): void => {
  el.textContent = '';
  for (const part of parseCertificateFieldTextParts(value, translate)) {
    if (part.token) el.appendChild(createPillElement(part.value));
    else if (part.value) el.appendChild(document.createTextNode(part.value));
  }
};

export const placeCaretAtEnd = (el: HTMLElement): void => {
  const selection = window.getSelection();
  const range = document.createRange();
  range.selectNodeContents(el);
  range.collapse(false);
  selection?.removeAllRanges();
  selection?.addRange(range);
};

export const getActiveRange = (el: HTMLElement): Range | null => {
  const selection = window.getSelection();
  return selection?.rangeCount && el.contains(selection.anchorNode)
    ? selection.getRangeAt(0).cloneRange()
    : null;
};

/** Inserts a node at the live caret, or where the user left off (savedRange). */
export const insertNodeAtCaret = (
  el: HTMLElement,
  node: Node,
  savedRange: Range | null,
): void => {
  const selection = window.getSelection();
  const liveRange =
    selection?.rangeCount && el.contains(selection.anchorNode)
      ? selection.getRangeAt(0).cloneRange()
      : null;
  let range = liveRange ?? savedRange;
  if (!range || !el.contains(range.startContainer)) {
    range = document.createRange();
    range.selectNodeContents(el);
    range.collapse(false);
  }
  el.focus();
  range.deleteContents();
  range.insertNode(node);
  range.setStartAfter(node);
  range.collapse(true);
  selection?.removeAllRanges();
  selection?.addRange(range);
};
