import { MiniMessageComponent, ResolvedStyle } from './mini-message.types';
import { hoverEventToTitle, mergeStyle, ownText, styleToNgStyle } from './mini-message.util';

/**
 * Escapes text for safe insertion into HTML. Always used on raw text content —
 * never skip this when building the string yourself.
 */
function escapeHtml(input: string): string {
  return input
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;');
}

function ngStyleToCss(style: Record<string, string>): string {
  return Object.entries(style)
    .map(([key, value]) => `${key}:${value}`)
    .join(';');
}

/**
 * Renders a component-JSON tree to an HTML string, e.g. for binding via
 * [innerHTML]="miniMessageToHtml(json) | ..." with Angular's DomSanitizer, or for use
 * outside Angular entirely. Prefer the <mini-message> component when you need click/hover
 * interactivity or the obfuscated animation — this string form is static (obfuscated text
 * renders as a fixed placeholder rather than an animated one).
 */
export function miniMessageToHtml(
  node: MiniMessageComponent,
  parentStyle?: ResolvedStyle
): string {
  const style = mergeStyle(parentStyle, node);
  const css = ngStyleToCss(styleToNgStyle(style));
  const text = escapeHtml(ownText(node));
  const title = hoverEventToTitle(node.hoverEvent);
  const titleAttr = title ? ` title="${escapeHtml(title)}"` : '';
  const insertionAttr = node.insertion ? ` data-insertion="${escapeHtml(node.insertion)}"` : '';

  let ownHtml: string;
  if (node.clickEvent?.action === 'open_url') {
    ownHtml = `<a href="${escapeHtml(
      node.clickEvent.value
    )}" target="_blank" rel="noopener noreferrer" class="mm-node mm-link" style="${css}"${titleAttr}${insertionAttr}>${text}</a>`;
  } else {
    const clickAttrs = node.clickEvent
      ? ` class="mm-node mm-clickable" data-click-action="${node.clickEvent.action}" data-click-value="${escapeHtml(
          node.clickEvent.value
        )}"`
      : ' class="mm-node"';
    ownHtml = `<span${clickAttrs} style="${css}"${titleAttr}${insertionAttr}>${text}</span>`;
  }

  const childrenHtml = (node.extra ?? []).map((child) => miniMessageToHtml(child, style)).join('');
  return ownHtml + childrenHtml;
}
