(function (root) {
  'use strict';

  function escapeText(value) {
    return String(value || '')
      .replace(/\\/g, '\\\\')
      .replace(/\r?\n/g, '\\n')
      .replace(/,/g, '\\,')
      .replace(/;/g, '\\;');
  }

  function calendarDay(value) {
    const match = String(value || '').match(/^(\d{4})-(\d{2})-(\d{2})/);
    return match ? match.slice(1).join('') : '';
  }

  function nextDay(day) {
    const date = new Date(Date.UTC(
      Number(day.slice(0, 4)), Number(day.slice(4, 6)) - 1, Number(day.slice(6, 8)) + 1
    ));
    return date.toISOString().slice(0, 10).replace(/-/g, '');
  }

  function uid(item) {
    const repository = String(item.repository || '').replace(/[^a-z0-9]+/gi, '-').replace(/^-|-$/g, '').toLowerCase();
    return `issue-${Number(item.number)}@${repository}`;
  }

  function foldLine(line) {
    const encoder = new TextEncoder();
    const chunks = [];
    let chunk = '';
    let bytes = 0;
    let limit = 75;
    for (const character of String(line)) {
      const width = encoder.encode(character).length;
      if (chunk && bytes + width > limit) {
        chunks.push(chunk);
        chunk = character;
        bytes = width;
        limit = 74;
      } else {
        chunk += character;
        bytes += width;
      }
    }
    chunks.push(chunk);
    return chunks.join('\r\n ');
  }

  function serializeAgendaCalendar(items, { generatedOn } = {}) {
    const stampDay = String(generatedOn || new Date().toISOString().slice(0, 10).replace(/-/g, ''));
    const lines = ['BEGIN:VCALENDAR', 'VERSION:2.0', 'PRODID:-//Stackchain//Agenda Snapshot//EN',
      'CALSCALE:GREGORIAN', 'METHOD:PUBLISH', 'X-WR-CALNAME:Stackchain Agenda'];
    (items || []).forEach(item => {
      const day = calendarDay(item.due_date);
      if (!day || !Number.isInteger(Number(item.number)) || !item.repository) return;
      lines.push(
        'BEGIN:VEVENT',
        `UID:${uid(item)}`,
        `DTSTAMP:${stampDay}T000000Z`,
        `DTSTART;VALUE=DATE:${day}`,
        `DTEND;VALUE=DATE:${nextDay(day)}`,
        `SUMMARY:${escapeText(item.title)}`,
        `DESCRIPTION:${escapeText(`${item.repository}#${item.number} · Stackchain Agenda snapshot`)}`,
        `URL:${String(item.url || '')}`,
        'TRANSP:TRANSPARENT',
        'END:VEVENT',
      );
    });
    lines.push('END:VCALENDAR');
    return lines.map(foldLine).join('\r\n') + '\r\n';
  }

  async function deliverCalendarSnapshot({
    text,
    filename,
    navigator,
    document,
    urlApi,
    FileCtor,
  }) {
    const file = new FileCtor([text], filename, { type: 'text/calendar;charset=utf-8' });
    const sharePayload = { files: [file], title: 'Stackchain Agenda', text: 'Agenda calendar snapshot' };
    if (typeof navigator?.share === 'function' && typeof navigator?.canShare === 'function' &&
        navigator.canShare(sharePayload)) {
      await navigator.share(sharePayload);
      return 'shared';
    }
    const href = urlApi.createObjectURL(file);
    try {
      const anchor = document.createElement('a');
      anchor.href = href;
      anchor.download = filename;
      anchor.click();
    } finally {
      urlApi.revokeObjectURL(href);
    }
    return 'downloaded';
  }

  function mountAgendaCalendarExport({
    qs,
    getItems,
    escapeHtml,
    onDone,
    windowObject = root,
    navigatorObject = root.navigator,
    documentObject = root.document,
    urlApi = root.URL,
    FileCtor = root.File,
  }) {
    const sheet = qs('#agenda-export-sheet');
    let items = [];
    let scrollY = 0;
    let trigger = null;
    const selectedItems = () => Array.from(qs('#agenda-export-items').querySelectorAll('input[type="checkbox"]'))
      .filter(checkbox => checkbox.checked)
      .map(checkbox => items[Number(checkbox.value)])
      .filter(Boolean);
    const updateSelection = () => {
      const selected = selectedItems();
      qs('#share-agenda-export').disabled = selected.length === 0;
      qs('#agenda-export-status').textContent = selected.length + ' of ' + items.length +
        (items.length === 1 ? ' deadline selected.' : ' deadlines selected.');
    };
    const close = () => {
      sheet.close();
      windowObject.scrollTo({ top:scrollY, behavior:'instant' });
      trigger?.focus();
    };
    qs('#open-agenda-export').addEventListener('click', event => {
      items = getItems();
      scrollY = windowObject.scrollY;
      trigger = event.currentTarget;
      qs('#agenda-export-items').innerHTML = items.map((item, index) =>
        '<label class="agenda-export-item"><input type="checkbox" value="' + index + '" checked> ' +
        '<span><strong>' + escapeHtml(item.title) + '</strong><small>' +
        escapeHtml(item.repository + '#' + item.number + ' · ' + item.due_date.slice(0, 10)) +
        '</small></span></label>'
      ).join('');
      qs('#agenda-export-items').querySelectorAll('input[type="checkbox"]').forEach(checkbox =>
        checkbox.addEventListener('change', updateSelection)
      );
      updateSelection();
      sheet.showModal();
      qs('#cancel-agenda-export').focus();
    });
    qs('#cancel-agenda-export').addEventListener('click', close);
    sheet.addEventListener('cancel', event => {
      event.preventDefault();
      close();
    });
    qs('#share-agenda-export').addEventListener('click', async () => {
      const selected = selectedItems();
      if (!selected.length) return;
      const button = qs('#share-agenda-export');
      button.disabled = true;
      qs('#agenda-export-status').textContent = 'Preparing calendar snapshot…';
      const day = new Date().toISOString().slice(0, 10);
      try {
        const text = serializeAgendaCalendar(selected, { generatedOn:day.replace(/-/g, '') });
        const result = await deliverCalendarSnapshot({
          text, filename:'stackchain-agenda-' + day + '.ics',
          navigator:navigatorObject, document:documentObject, urlApi, FileCtor,
        });
        onDone(result);
        close();
      } catch (error) {
        qs('#agenda-export-status').textContent = error?.name === 'AbortError' ?
          'Share cancelled. Nothing was exported.' : 'Calendar export failed. Retry without leaving Agenda.';
        button.disabled = false;
      }
    });
  }

  const api = { calendarDay, deliverCalendarSnapshot, escapeText, foldLine, mountAgendaCalendarExport, serializeAgendaCalendar };
  if (typeof module !== 'undefined' && module.exports) module.exports = api;
  else root.StackchainAgendaCalendar = api;
})(typeof window !== 'undefined' ? window : globalThis);
