function createTodaySummary({
  storage = null, getLogin = () => '', share = null, copy = null,
  resolveTarget = null, enqueueDurably = null,
  createOperationId = () => globalThis.crypto?.randomUUID?.() || String(Date.now()) + '-' + Math.random().toString(16).slice(2),
} = {}) {
  let draft = null;
  let draftKey = '';
  let postPromise = null;

  const storageKey = () => {
    const login = String(getLogin() || '').trim().toLowerCase();
    return login ? 'stackchain.today-summary-draft.v1.' + login : '';
  };
  const syncAccount = () => {
    const key = storageKey();
    if (key !== draftKey) {
      draft = null;
      draftKey = key;
    }
  };
  const copyRows = rows => rows.map(row => ({ ...row }));
  const snapshot = () => {
    syncAccount();
    return draft ? {
      ...draft,
      worked:copyRows(draft.worked),
      tomorrow:copyRows(draft.tomorrow),
      target:draft.target ? { ...draft.target } : null,
    } : null;
  };
  const persist = () => {
    const key = storageKey();
    if (storage && key && draft) storage.setItem(key, JSON.stringify(draft));
  };
  const validRows = (rows, includeTime) => Array.isArray(rows) && rows.length <= 20 && rows.every(row =>
    row && typeof row.identity === 'string' && row.identity.length > 0 && row.identity.length <= 500 &&
    typeof row.title === 'string' && row.title.length <= 180 && typeof row.context === 'string' &&
    row.context.length <= 180 && typeof row.include === 'boolean' && (!includeTime ||
      (Number.isInteger(row.actual_minutes) && row.actual_minutes >= 0 && row.actual_minutes <= 1440))
  );
  const load = () => {
    const key = storageKey();
    if (!storage || !key) return null;
    try {
      const saved = JSON.parse(storage.getItem(key) || 'null');
      const validTarget = Boolean(saved) && (saved.target === null || saved.target === undefined || (saved.target &&
        typeof saved.target.repository === 'string' && /^[a-z0-9_.-]+\/[a-z0-9_.-]+$/.test(saved.target.repository) &&
        Number.isInteger(saved.target.number) && saved.target.number > 0 &&
        ['issue', 'pull'].includes(saved.target.kind) && typeof saved.target.title === 'string' &&
        typeof saved.target.state === 'string'));
      if (!saved || !validRows(saved.worked, true) || !validRows(saved.tomorrow, false) ||
          typeof saved.include_actuals !== 'boolean' || typeof saved.note !== 'string' || saved.note.length > 1000 ||
          !['string', 'undefined'].includes(typeof saved.destination) || !validTarget ||
          !['string', 'undefined'].includes(typeof saved.operation_id)) {
        if (saved !== null) storage.removeItem(key);
        return null;
      }
      return saved;
    } catch (_error) {
      try { storage.removeItem(key); } catch (_ignored) {}
      return null;
    }
  };
  const cleanRow = (row, includeTime) => {
    const cleaned = {
      identity:String(row.identity),
      title:String(row.title || row.label || row.identity).slice(0, 180),
      context:String(row.context || '').slice(0, 180),
    };
    if (includeTime) cleaned.actual_minutes = Math.min(1440, Math.max(0, Number(row.actual_minutes) || 0));
    cleaned.include = true;
    return cleaned;
  };
  const selectedLines = (rows, showTime = false) => rows.filter(row => row.include).map(row =>
    '- ' + row.title + (showTime ? ' (' + row.actual_minutes + 'm)' : '')
  );
  const discard = () => {
    const key = storageKey();
    if (storage && key) storage.removeItem(key);
    draft = null;
  };

  return {
    begin(worked, tomorrow) {
      draftKey = storageKey();
      draft = {
        worked:(worked || []).filter(row => row?.identity).slice(0, 20).map(row => cleanRow(row, true)),
        tomorrow:(tomorrow || []).filter(row => row?.identity).slice(0, 20).map(row => cleanRow(row, false)),
        include_actuals:false,
        note:'',
        destination:'',
        target:null,
        operation_id:'',
      };
      persist();
      return snapshot();
    },
    snapshot,
    restore() {
      draftKey = storageKey();
      draft = load();
      return Boolean(draft);
    },
    choose(section, identity, include) {
      syncAccount();
      const row = draft?.[section]?.find(candidate => candidate.identity === identity);
      if (!row || typeof include !== 'boolean') return false;
      row.include = include;
      persist();
      return true;
    },
    includeActuals(include) {
      syncAccount();
      if (!draft || typeof include !== 'boolean') return false;
      draft.include_actuals = include;
      persist();
      return true;
    },
    setNote(note) {
      syncAccount();
      if (!draft) return false;
      draft.note = String(note || '').trim().slice(0, 1000);
      persist();
      return true;
    },
    setDestination(value) {
      syncAccount();
      if (!draft) return null;
      const match = String(value || '').trim().match(/^([a-z0-9_.-]+)\s*\/\s*([a-z0-9_.-]+)\s*#\s*([1-9][0-9]*)$/i);
      draft.destination = String(value || '').trim().slice(0, 260);
      draft.target = null;
      draft.operation_id = '';
      persist();
      if (!match) return null;
      const repository = (match[1] + '/' + match[2]).toLowerCase();
      return { repository, number:Number(match[3]), label:repository + '#' + Number(match[3]) };
    },
    async validateDestination() {
      syncAccount();
      const parsed = this.setDestination(draft?.destination || '');
      if (!parsed) throw new Error('Enter a destination like owner/repo#42.');
      if (typeof resolveTarget !== 'function') throw new Error('Destination validation is unavailable.');
      const resolved = await resolveTarget({ repository:parsed.repository, number:parsed.number });
      if (!resolved || resolved.repository !== parsed.repository || Number(resolved.number) !== parsed.number ||
          !['issue', 'pull'].includes(resolved.kind)) throw new Error('Choose an exact visible issue or pull request.');
      draft.target = {
        repository:parsed.repository, number:parsed.number, kind:resolved.kind,
        title:String(resolved.title || '').slice(0, 180), state:String(resolved.state || ''),
      };
      draft.operation_id = String(createOperationId()).slice(0, 128);
      persist();
      return { ...draft.target };
    },
    postSummary() {
      if (postPromise) return postPromise;
      syncAccount();
      const body = this.text();
      if (!body) throw new Error('Select at least one summary item or add a note.');
      if (!draft?.target || !draft.operation_id) throw new Error('Validate an exact Gitea destination first.');
      if (typeof enqueueDurably !== 'function') throw new Error('Gitea posting is unavailable.');
      const target = { ...draft.target };
      const message = {
        kind:'search-reply', repository:target.repository, number:target.number,
        targetKind:target.kind, body, operationId:draft.operation_id,
      };
      postPromise = (async () => {
        try {
          const admission = await enqueueDurably(message);
          discard();
          return { status:'queued', durability:admission?.durability || 'foreground-only', target };
        } finally {
          postPromise = null;
        }
      })();
      return postPromise;
    },
    discard,
    text() {
      syncAccount();
      if (!draft) return '';
      const sections = [];
      const worked = selectedLines(draft.worked, draft.include_actuals);
      const tomorrow = selectedLines(draft.tomorrow);
      if (worked.length) sections.push(['Today', ...worked].join('\n'));
      if (tomorrow.length) sections.push(['Tomorrow', ...tomorrow].join('\n'));
      if (draft.note) sections.push('Note\n' + draft.note);
      return sections.join('\n\n');
    },
    async shareSummary() {
      const text = this.text();
      if (!text) throw new Error('Select at least one summary item or add a note.');
      if (typeof share === 'function') {
        try {
          await share({ title:'Today summary', text });
          discard();
          return { status:'shared' };
        } catch (error) {
          if (error?.name === 'AbortError') return { status:'canceled' };
          throw error;
        }
      }
      if (typeof copy !== 'function') throw new Error('Sharing is unavailable on this device.');
      await copy(text);
      discard();
      return { status:'copied' };
    },
  };
}

function createTodaySummaryView({ summary, qs, escapeHtml }) {
  const rowMarkup = (section, row) => '<label class="today-summary-item"><input type="checkbox" data-summary-section="' +
    section + '" data-summary-identity="' + escapeHtml(row.identity) + '"' + (row.include ? ' checked' : '') +
    '><span><strong>' + escapeHtml(row.title) + '</strong><span class="small muted">' +
    escapeHtml(row.context) + '</span></span></label>';

  function close() {
    qs('#today-summary-sheet').hidden = true;
    document.body.classList.remove('task-overlay-open');
    qs('#start-work-session')?.focus();
  }

  function render() {
    const draft = summary.snapshot();
    if (!draft) return;
    qs('#today-summary-worked').innerHTML = draft.worked.map(row => rowMarkup('worked', row)).join('') ||
      '<p class="small muted">No worked items selected.</p>';
    qs('#today-summary-tomorrow').innerHTML = draft.tomorrow.map(row => rowMarkup('tomorrow', row)).join('') ||
      '<p class="small muted">Nothing scheduled for tomorrow.</p>';
    qs('#today-summary-include-actuals').checked = draft.include_actuals;
    qs('#today-summary-note').value = draft.note;
    qs('#today-summary-preview').textContent = summary.text();
    qs('#today-summary-destination').value = draft.destination || '';
    qs('#today-summary-target').textContent = draft.target ?
      (draft.target.repository + '#' + draft.target.number + ' · ' + draft.target.kind + ' · ' + draft.target.title) : '';
    qs('#post-today-summary').disabled = !draft.target;
    qs('#today-summary-sheet').querySelectorAll('[data-summary-identity]').forEach(input => {
      input.addEventListener('change', () => {
        summary.choose(input.dataset.summarySection, input.dataset.summaryIdentity, input.checked);
        render();
      });
    });
  }

  function show() {
    qs('#today-summary-status').textContent = '';
    render();
    qs('#today-summary-sheet').hidden = false;
    document.body.classList.add('task-overlay-open');
    requestAnimationFrame(() => (qs('#today-summary-worked input') || qs('#share-today-summary')).focus());
  }

  function open(worked, tomorrow) {
    summary.begin(worked, tomorrow);
    show();
  }

  async function shareDraft(button) {
    button.disabled = true;
    qs('#today-summary-status').textContent = 'Opening share options…';
    try {
      const result = await summary.shareSummary();
      if (result.status === 'canceled') {
        qs('#today-summary-status').textContent = 'Share canceled. Your private draft is still here.';
        return;
      }
      qs('#my-work-action-status').textContent = result.status === 'shared' ?
        'Today summary shared.' : 'Today summary copied to your clipboard.';
      close();
    } catch (error) {
      qs('#today-summary-status').textContent = error.message || 'Summary could not be shared. Your draft is unchanged.';
    } finally {
      button.disabled = false;
    }
  }

  async function validateDestination(button) {
    button.disabled = true;
    qs('#today-summary-status').textContent = 'Checking destination…';
    try {
      await summary.validateDestination();
      render();
      qs('#today-summary-status').textContent = 'Destination confirmed. Review it, then post once.';
    } catch (error) {
      qs('#today-summary-status').textContent = error.message || 'Destination could not be confirmed.';
    } finally {
      button.disabled = false;
    }
  }

  async function postDraft(button) {
    button.disabled = true;
    qs('#today-summary-status').textContent = 'Saving comment for delivery…';
    try {
      const result = await summary.postSummary();
      qs('#my-work-action-status').textContent = 'Today summary queued for ' +
        result.target.repository + '#' + result.target.number + '.';
      close();
    } catch (error) {
      qs('#today-summary-status').textContent = error.message || 'Summary could not be queued. Your review is unchanged.';
      button.disabled = false;
    }
  }

  function bind() {
    qs('#close-today-summary').addEventListener('click', close);
    qs('#discard-today-summary').addEventListener('click', () => { summary.discard(); close(); });
    qs('#today-summary-include-actuals').addEventListener('change', event => {
      summary.includeActuals(event.currentTarget.checked); render();
    });
    qs('#today-summary-note').addEventListener('input', event => {
      summary.setNote(event.currentTarget.value);
      qs('#today-summary-preview').textContent = summary.text();
    });
    qs('#today-summary-destination').addEventListener('input', event => {
      summary.setDestination(event.currentTarget.value);
      qs('#today-summary-target').textContent = '';
      qs('#post-today-summary').disabled = true;
    });
    qs('#validate-today-summary-destination').addEventListener('click', event => validateDestination(event.currentTarget));
    qs('#post-today-summary').addEventListener('click', event => postDraft(event.currentTarget));
    qs('#share-today-summary').addEventListener('click', event => shareDraft(event.currentTarget));
  }

  return {
    open, close, render, bind, shareDraft, validateDestination, postDraft,
    resume() { if (summary.restore()) show(); },
  };
}

function setupTodaySummary({ qs, escapeHtml, getLogin, resolveTarget, enqueueDurably, fetchJson }) {
  const targetResolver = resolveTarget || (async target => {
    if (typeof fetchJson !== 'function') throw new Error('Destination validation is unavailable.');
    const item = await fetchJson('api/v1/repos/' + target.repository + '/issues/' + target.number +
      '/preview?kind=issue');
    if (String(item.repository || '').toLowerCase() !== target.repository ||
        Number(item.number) !== target.number || !['issue', 'pull'].includes(item.kind)) {
      throw new Error('That exact issue or pull request is not visible to this account.');
    }
    return { repository:target.repository, number:target.number, kind:item.kind,
      title:item.title || '', state:item.state || '' };
  });
  const summary = createTodaySummary({
    storage:localStorage,
    getLogin,
    share:typeof navigator.share === 'function' ? payload => navigator.share(payload) : null,
    copy:typeof navigator.clipboard?.writeText === 'function' ? text => navigator.clipboard.writeText(text) : null,
    resolveTarget:targetResolver,
    enqueueDurably,
  });
  const view = createTodaySummaryView({ summary, qs, escapeHtml });
  view.bind();
  return view;
}

if (typeof module !== 'undefined' && module.exports) {
  createTodaySummary.createView = createTodaySummaryView;
  createTodaySummary.setup = setupTodaySummary;
  module.exports = createTodaySummary;
}
