(() => {
  'use strict';

  const PRINT_FRAME_CLASS = 'foodord-live-orders-print-frame';
  const PRINT_FRAME_TIMEOUT = 300000;

  function text(value) {
    return String(value ?? '').trim();
  }

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

    const parsed = Number(value);
    return Number.isFinite(parsed) ? parsed : null;
  }

  function positiveInteger(value, fallback = 0) {
    const parsed = Number.parseInt(value, 10);
    return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
  }

  function normalizeLocale(value) {
    const locale = text(value).replaceAll('_', '-');
    if (!locale) {
      return undefined;
    }

    try {
      return Intl.DateTimeFormat.supportedLocalesOf([locale])[0];
    } catch {
      return undefined;
    }
  }

  function escapeHtml(value) {
    return String(value ?? '')
      .replaceAll('&', '&amp;')
      .replaceAll('<', '&lt;')
      .replaceAll('>', '&gt;')
      .replaceAll('"', '&quot;')
      .replaceAll("'", '&#039;');
  }

  function displayValue(value) {
    if (Array.isArray(value)) {
      return value.map(displayValue).filter(Boolean).join(', ');
    }

    if (value && typeof value === 'object') {
      return text(
        value.display_value ??
          value.label ??
          value.name ??
          value.value ??
          ''
      );
    }

    return text(value);
  }

  function formatMoney(value, currency, locale, currencyFormat = null) {
    const amount = number(value);
    if (amount === null) {
      return text(value);
    }

    if (currencyFormat && typeof currencyFormat === 'object') {
      const parsedDecimals = Number.parseInt(currencyFormat.decimals, 10);
      const decimals = Number.isFinite(parsedDecimals)
        ? Math.max(0, Math.min(parsedDecimals, 8))
        : 2;
      const [integerPart, fractionPart = ''] = amount
        .toFixed(decimals)
        .split('.');
      const sign = integerPart.startsWith('-') ? '-' : '';
      const unsignedInteger = sign ? integerPart.slice(1) : integerPart;
      const thousandSeparator = String(
        currencyFormat.thousandSeparator ?? ','
      );
      const decimalSeparator = String(
        currencyFormat.decimalSeparator ?? '.'
      );
      const groupedInteger = thousandSeparator
        ? unsignedInteger.replace(
            /\B(?=(\d{3})+(?!\d))/gu,
            thousandSeparator
          )
        : unsignedInteger;
      const formattedAmount = `${sign}${groupedInteger}${
        decimals > 0 ? `${decimalSeparator}${fractionPart}` : ''
      }`;
      const currencyCode = text(
        currency || currencyFormat.currency
      ).toUpperCase();
      let symbol =
        currencyCode === text(currencyFormat.currency).toUpperCase()
          ? text(currencyFormat.symbol)
          : '';

      if (!symbol && currencyCode) {
        try {
          symbol =
            new Intl.NumberFormat(normalizeLocale(locale), {
              style: 'currency',
              currency: currencyCode,
              minimumFractionDigits: 0,
              maximumFractionDigits: 0,
            })
              .formatToParts(0)
              .find((part) => part.type === 'currency')?.value || currencyCode;
        } catch {
          symbol = currencyCode;
        }
      }

      switch (text(currencyFormat.position)) {
        case 'right':
          return `${formattedAmount}${symbol}`;
        case 'right_space':
          return `${formattedAmount} ${symbol}`.trim();
        case 'left_space':
          return `${symbol} ${formattedAmount}`.trim();
        default:
          return `${symbol}${formattedAmount}`;
      }
    }

    try {
      return new Intl.NumberFormat(normalizeLocale(locale), {
        style: 'currency',
        currency: text(currency) || 'EUR',
      }).format(amount);
    } catch {
      return `${amount.toFixed(2)} ${text(currency)}`.trim();
    }
  }

  function parseDate(value) {
    const raw = text(value);
    if (!raw) {
      return null;
    }

    const normalized = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}/.test(raw)
      ? raw.replace(' ', 'T')
      : raw;
    const parsed = new Date(normalized);
    return Number.isNaN(parsed.getTime()) ? null : parsed;
  }

  function formatDateTime(value, locale) {
    const parsed = parseDate(value);
    if (!parsed) {
      return text(value);
    }

    try {
      return new Intl.DateTimeFormat(normalizeLocale(locale), {
        dateStyle: 'short',
        timeStyle: 'short',
      }).format(parsed);
    } catch {
      return text(value);
    }
  }

  function formatTime(value, locale) {
    const parsed = parseDate(value);
    if (!parsed) {
      return text(value);
    }

    try {
      return new Intl.DateTimeFormat(normalizeLocale(locale), {
        hour: '2-digit',
        hour12: false,
        hourCycle: 'h23',
        minute: '2-digit',
      }).format(parsed);
    } catch {
      return text(value);
    }
  }

  function getOrderItems(order) {
    if (Array.isArray(order?.items)) {
      return order.items;
    }
    if (Array.isArray(order?.line_items)) {
      return order.line_items;
    }
    return [];
  }

  function getFulfillment(order) {
    const mode = text(
      order?.fulfillment_type || order?.mode || order?.order_mode
    ).toLowerCase();
    return mode === 'pickup' ? 'pickup' : 'delivery';
  }

  function getCustomerName(order) {
    return (
      text(order?.customer?.name) ||
      text(
        `${text(order?.billing?.first_name)} ${text(
          order?.billing?.last_name
        )}`
      )
    );
  }

  function getAddressLines(order) {
    if (getFulfillment(order) === 'pickup') {
      return [];
    }

    const source = order?.shipping || order?.billing || {};
    const primary = [text(source.address_1), text(source.address_2)]
      .filter(Boolean)
      .join(', ');
    const locality = [
      text(source.postcode),
      text(source.city),
      text(source.state),
      text(source.country),
    ]
      .filter(Boolean)
      .join(' ');

    return [primary, locality].filter(Boolean);
  }

  function getRequestedTime(order) {
    return text(
      order?.requested_time_display ||
        order?.requested_time ||
        order?.delivery_time ||
        order?.pickup_time
    );
  }

  function getConfirmedTime(order, locale, strings) {
    const confirmation = order?.confirmation || {};
    const confirmedDisplay =
      text(confirmation.confirmed_display) ||
      text(order?.restaurant_confirmed_display);
    if (confirmedDisplay) {
      return confirmedDisplay;
    }

    const explicit =
      text(confirmation.confirmed_slot_local) ||
      text(confirmation.confirmed_slot) ||
      text(order?.restaurant_confirmed_time);
    if (explicit) {
      return formatTime(explicit, locale);
    }

    const minutes = positiveInteger(
      confirmation.confirmed_minutes ??
        order?.restaurant_confirmed_minutes
    );
    const confirmedAt =
      text(confirmation.confirmed_at_local) ||
      text(confirmation.confirmed_at);
    const confirmedDate = parseDate(confirmedAt);
    if (minutes > 0 && confirmedDate) {
      confirmedDate.setMinutes(confirmedDate.getMinutes() + minutes);
      return formatTime(confirmedDate.toISOString(), locale);
    }

    return (
      text(confirmation.confirmed_display) ||
      text(order?.restaurant_confirmed_display) ||
      strings.pending ||
      'Pending'
    );
  }

  function buildSection(lines, className = '') {
    const filtered = lines.filter(Boolean);
    if (filtered.length === 0) {
      return '';
    }

    return `<section class="foodord-receipt-section ${escapeHtml(
      className
    )}">${filtered
      .map(
        (line) =>
          `<div class="foodord-receipt-section__line">${escapeHtml(
            line
          )}</div>`
      )
      .join('')}</section>`;
  }

  function buildToppingMarkup(
    item,
    currency,
    locale,
    strings,
    showGroupLabels,
    currencyFormat
  ) {
    const sets = Array.isArray(item?.toppings) ? item.toppings : [];
    return sets
      .map((set) => {
        const choices = Array.isArray(set?.items) ? set.items : [];
        const values = choices
          .map((choice) => {
            const label =
              typeof choice === 'string'
                ? text(choice)
                : text(choice?.label || choice?.name || choice?.value);
            if (!label) {
              return '';
            }

            const price =
              choice && typeof choice === 'object'
                ? number(
                    choice.price ??
                      choice.extra ??
                      choice.amount ??
                      choice.total
                  )
                : null;
            const priceText =
              price !== null && price !== 0
                ? ` (+${formatMoney(
                    price,
                    currency,
                    locale,
                    currencyFormat
                  )})`
                : '';
            return `<div class="foodord-receipt-item__meta-value">+ ${escapeHtml(
              `${label}${priceText}`
            )}</div>`;
          })
          .filter(Boolean)
          .join('');
        if (!values) {
          return '';
        }

        const label =
          text(set?.set_name || set?.name || set?.label) ||
          strings.toppings ||
          'Toppings';
        const labelMarkup = showGroupLabels
          ? `<div class="foodord-receipt-item__meta-label">${escapeHtml(
              label
            )}:</div>`
          : '';
        return `${labelMarkup}${values}`;
      })
      .join('');
  }

  function buildItemMetaMarkup(item) {
    const entries = Array.isArray(item?.meta_data) ? item.meta_data : [];
    return entries
      .map((entry) => {
        const label = text(entry?.display_key || entry?.key);
        const value = displayValue(entry?.display_value ?? entry?.value);
        if (!value || label.startsWith('_')) {
          return '';
        }

        return `<div class="foodord-receipt-item__meta-value">${escapeHtml(
          label ? `${label}: ${value}` : value
        )}</div>`;
      })
      .join('');
  }

  function buildItemsMarkup(
    order,
    locale,
    strings,
    showToppingGroupLabels,
    currencyFormat
  ) {
    const items = getOrderItems(order);
    if (items.length === 0) {
      return `<div class="foodord-receipt-empty">${escapeHtml(
        strings.noItems || 'No items'
      )}</div>`;
    }

    const currency = text(order?.currency) || 'EUR';
    return items
      .map((item) => {
        const quantity = positiveInteger(item?.quantity, 1);
        const total =
          item?.line_total_incl_tax ??
          item?.total_incl_tax ??
          item?.line_total ??
          item?.total;
        const metaMarkup = buildItemMetaMarkup(item);
        const toppingMarkup = buildToppingMarkup(
          item,
          currency,
          locale,
          strings,
          showToppingGroupLabels,
          currencyFormat
        );
        const note = text(item?.note || item?.foodord_note);

        return `
          <article class="foodord-receipt-item">
            <div class="foodord-receipt-item__row">
              <div class="foodord-receipt-item__info">
                <strong class="foodord-receipt-item__quantity">${escapeHtml(
                  quantity
                )} ×</strong>
                <span class="foodord-receipt-item__name">${escapeHtml(
                  item?.name || strings.item || 'Item'
                )}</span>
              </div>
              <span class="foodord-receipt-item__total">${escapeHtml(
                formatMoney(total, currency, locale, currencyFormat)
              )}</span>
            </div>
            ${metaMarkup}
            ${toppingMarkup}
            ${
              note
                ? `<div class="foodord-receipt-item__note"><strong>${escapeHtml(
                    strings.note || strings.notes || 'Note'
                  )}:</strong><span>${escapeHtml(note)}</span></div>`
                : ''
            }
          </article>
        `;
      })
      .join('');
  }

  function buildTotalsMarkup(order, locale, strings, currencyFormat) {
    const currency = text(order?.currency) || 'EUR';
    const rows = [];
    const addRow = (label, value, modifier = '', force = false) => {
      const amount = number(value);
      if (amount === null || (!force && amount <= 0)) {
        return;
      }

      rows.push({
        label,
        value: `${modifier === 'discount' ? '-' : ''}${formatMoney(
          Math.abs(amount),
          currency,
          locale,
          currencyFormat
        )}`,
        modifier,
      });
    };

    addRow(
      strings.subtotal || 'Subtotal',
      order?.subtotal_incl_tax ?? order?.subtotal,
      '',
      true
    );

    const taxLines = Array.isArray(order?.tax_lines)
      ? order.tax_lines
      : Array.isArray(order?.taxLines)
        ? order.taxLines
        : [];
    taxLines.forEach((entry) => {
      const rate = number(entry?.rate_percent ?? entry?.rate);
      const rawLabel =
        text(entry?.label || entry?.name) || strings.tax || 'VAT';
      const label =
        rate !== null && !rawLabel.includes('%')
          ? `${rawLabel} (${Number(rate.toFixed(3))}%)`
          : rawLabel;
      addRow(label, entry?.total ?? entry?.tax_total ?? entry?.amount);
    });

    if (getFulfillment(order) !== 'pickup') {
      addRow(
        strings.deliveryFee || 'Delivery fee',
        order?.delivery_fee ?? order?.shipping_total
      );
    }
    addRow(
      strings.tip || 'Tip',
      order?.tip_amount ?? order?.tip_total ?? order?.tip
    );
    addRow(
      strings.discount || 'Discount',
      order?.discount_total,
      'discount'
    );
    addRow(strings.total || 'Total', order?.total, 'total', true);

    return `<div class="foodord-receipt-totals">${rows
      .map(
        (row) => `
          <div class="foodord-receipt-totals__row${
            row.modifier
              ? ` foodord-receipt-totals__row--${escapeHtml(row.modifier)}`
              : ''
          }">
            <span>${escapeHtml(row.label)}</span>
            <strong>${escapeHtml(row.value)}</strong>
          </div>
        `
      )
      .join('')}</div>`;
  }

  function buildReceiptHtml(order, options = {}) {
    const strings = options.strings || {};
    const locale = options.locale || '';
    const direction = options.direction === 'rtl' ? 'rtl' : 'ltr';
    const orderNumber = text(order?.number || order?.id);
    const fulfillment = getFulfillment(order);
    const fulfillmentLabel =
      fulfillment === 'pickup'
        ? strings.pickup || 'Pickup'
        : strings.delivery || 'Delivery';
    const customerLines = [];
    const customerName = getCustomerName(order);
    const customerPhone = text(
      order?.customer?.phone || order?.billing?.phone
    );
    const addressLines = getAddressLines(order);

    if (customerName) {
      customerLines.push(`${strings.name || 'Name'}: ${customerName}`);
    }
    if (customerPhone) {
      customerLines.push(`${strings.phone || 'Phone'}: ${customerPhone}`);
    }
    if (addressLines.length > 0) {
      customerLines.push(
        `${strings.address || 'Address'}: ${addressLines[0]}`
      );
      customerLines.push(...addressLines.slice(1));
    }

    const requestedTime = getRequestedTime(order);
    const confirmedTime = getConfirmedTime(order, locale, strings);
    const paymentMethod = text(order?.payment_method_title);
    const note = text(order?.customer_note || order?.note);
    const stylesUrl = text(options.stylesUrl);

    return `<!doctype html>
<html lang="${escapeHtml(options.language || 'en')}" dir="${direction}">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>${escapeHtml(`#${orderNumber}`)}</title>
    ${stylesUrl ? `<link rel="stylesheet" href="${escapeHtml(stylesUrl)}">` : ''}
  </head>
  <body>
    <main class="foodord-receipt">
      <header class="foodord-receipt__header">
        <div class="foodord-receipt__fulfillment">${escapeHtml(
          fulfillmentLabel
        )}</div>
        <div class="foodord-receipt__order-number">#${escapeHtml(
          orderNumber
        )}</div>
        ${
          order?.date
            ? `<div class="foodord-receipt__placed">${escapeHtml(
                strings.placed || 'Placed'
              )}: ${escapeHtml(
                text(order?.date_display) ||
                  formatDateTime(order.date, locale)
              )}</div>`
            : ''
        }
        ${
          requestedTime
            ? `<div class="foodord-receipt__schedule">${escapeHtml(
                strings.customerTime || 'Customer time'
              )}: <strong>${escapeHtml(requestedTime)}</strong></div>`
            : ''
        }
        ${
          confirmedTime
            ? `<div class="foodord-receipt__schedule foodord-receipt__schedule--confirmed"><span>${escapeHtml(
                strings.restaurantTime || 'Restaurant confirmed time'
              )}:</span><strong>${escapeHtml(confirmedTime)}</strong></div>`
            : ''
        }
      </header>
      <div class="foodord-receipt__divider"></div>
      ${buildSection(customerLines, 'foodord-receipt-section--customer')}
      ${
        customerLines.length > 0
          ? '<div class="foodord-receipt__divider"></div>'
          : ''
      }
      <section class="foodord-receipt-items">
        ${buildItemsMarkup(
          order,
          locale,
          strings,
          options.showToppingGroupLabels !== false,
          options.currencyFormat
        )}
      </section>
      <div class="foodord-receipt__divider"></div>
      ${buildTotalsMarkup(order, locale, strings, options.currencyFormat)}
      ${
        paymentMethod
          ? `<div class="foodord-receipt__divider"></div><section class="foodord-receipt-payment">${escapeHtml(
              paymentMethod
            )}</section>`
          : ''
      }
      ${
        note
          ? `<section class="foodord-receipt-note"><strong>${escapeHtml(
              strings.note || strings.notes || 'Note'
            )}:</strong><span>${escapeHtml(note)}</span></section>`
          : ''
      }
    </main>
  </body>
</html>`;
  }

  function createPrintFrame() {
    const frame = document.createElement('iframe');
    frame.className = PRINT_FRAME_CLASS;
    frame.setAttribute('aria-hidden', 'true');
    frame.tabIndex = -1;
    document.body.appendChild(frame);
    return frame;
  }

  function createJob() {
    const frame = createPrintFrame();
    let active = true;
    let cleanupTimer = null;

    const cleanup = () => {
      if (!active) {
        return;
      }
      active = false;
      if (cleanupTimer) {
        window.clearTimeout(cleanupTimer);
      }
      frame.remove();
    };

    const print = (order, options = {}) =>
      new Promise((resolve, reject) => {
        if (!active || !order || typeof order !== 'object') {
          reject(new Error('invalid-print-job'));
          return;
        }

        frame.addEventListener(
          'load',
          () => {
            const printWindow = frame.contentWindow;
            if (!printWindow) {
              cleanup();
              reject(new Error('print-frame-unavailable'));
              return;
            }

            const executePrint = async () => {
              try {
                if (printWindow.document.fonts?.ready) {
                  await printWindow.document.fonts.ready;
                }
                printWindow.addEventListener('afterprint', cleanup, {
                  once: true,
                });
                cleanupTimer = window.setTimeout(
                  cleanup,
                  PRINT_FRAME_TIMEOUT
                );
                printWindow.focus();
                printWindow.print();
                resolve();
              } catch (error) {
                cleanup();
                reject(error);
              }
            };

            window.requestAnimationFrame(() => {
              window.requestAnimationFrame(executePrint);
            });
          },
          { once: true }
        );

        frame.srcdoc = buildReceiptHtml(order, options);
      });

    return { cancel: cleanup, print };
  }

  window.FoodordLiveOrdersReceipt = Object.freeze({
    buildReceiptHtml,
    createJob,
    print(order, options = {}) {
      return createJob().print(order, options);
    },
  });
})();
