import type {
  NotificationBodyFormat,
  NotificationRenderResult,
  NotificationTemplateRecord,
} from '../types';

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

function resolveTokenValue(payload: Record<string, unknown>, token: string): string {
  const value = token.split('.').reduce<unknown>((current, part) => {
    if (!current || typeof current !== 'object') {
      return undefined;
    }

    return (current as Record<string, unknown>)[part];
  }, payload);

  if (value === null || value === undefined || value === '') {
    return '-';
  }

  if (typeof value === 'string') {
    return value;
  }

  if (typeof value === 'number' || typeof value === 'boolean') {
    return String(value);
  }

  return '-';
}

export function extractTemplateVariables(template: string | null): string[] {
  if (!template) {
    return [];
  }

  return [...new Set(Array.from(template.matchAll(TOKEN_PATTERN), (match) => match[1]))];
}

export function renderTemplateString(
  template: string | null,
  payload: Record<string, unknown>,
): string | null {
  if (!template) {
    return null;
  }

  return template.replace(TOKEN_PATTERN, (_match, token: string) =>
    resolveTokenValue(payload, token),
  );
}

function toPlainText(body: string, format: NotificationBodyFormat): string {
  if (format !== 'html') {
    return body;
  }

  return body
    .replace(/<br\s*\/?>/gi, '\n')
    .replace(/<\/p>/gi, '\n\n')
    .replace(/<[^>]+>/g, '')
    .replace(/\n{3,}/g, '\n\n')
    .trim();
}

export function renderNotificationTemplate(
  template: Pick<
    NotificationTemplateRecord,
    | 'subjectTemplate'
    | 'titleTemplate'
    | 'bodyTemplate'
    | 'bodyFormat'
    | 'linkTemplate'
  >,
  payload: Record<string, unknown>,
): NotificationRenderResult {
  const subject =
    renderTemplateString(template.subjectTemplate ?? template.titleTemplate, payload) ?? '-';
  const title = renderTemplateString(template.titleTemplate, payload) ?? subject;
  const renderedBody = renderTemplateString(template.bodyTemplate, payload) ?? '-';
  const bodyHtml = template.bodyFormat === 'html' ? renderedBody : null;
  const bodyText = toPlainText(renderedBody, template.bodyFormat);

  return {
    subject,
    title,
    bodyText: bodyText || '-',
    bodyHtml,
    linkUrl: renderTemplateString(template.linkTemplate, payload),
    variables: [
      ...new Set([
        ...extractTemplateVariables(template.subjectTemplate),
        ...extractTemplateVariables(template.titleTemplate),
        ...extractTemplateVariables(template.bodyTemplate),
        ...extractTemplateVariables(template.linkTemplate),
      ]),
    ],
  };
}
