function createAuthoredOutbox({ storage, fetchJson, coordinator, backgroundSync, getOwnerLogin = () => '', createOperationId, mergeChecklistConflict, now = () => Date.now(), maxItems = 50 }) {
  const storageKey = 'stackchain.authored-outbox.v1';
  const makeId = createOperationId || (() =>
    globalThis.crypto?.randomUUID?.() || String(Date.now()) + '-' + Math.random().toString(16).slice(2)
  );
  const pending = new Map();
  const supportedKinds = new Set(['issue-comment', 'pull-comment', 'search-reply', 'update-reply', 'update-reply-read', 'pull-review', 'issue-close', 'issue-blocker', 'issue-content']);

  function messageAttachments(message) {
    const values = Array.isArray(message?.attachments) ? message.attachments :
      (Array.isArray(message?.attachment) ? message.attachment : (message?.attachment ? [message.attachment] : []));
    return values.filter(Boolean).slice(0, 5);
  }

  function attachmentMetadata(value) {
    const note = String(value.note || '').replace(/\s+/g, ' ').trim().slice(0, 240);
    return {
      filename: String(value.filename || ''),
      contentType: String(value.contentType || ''),
      stored: true,
      ...(note ? { note } : {}),
      ...(value.operationId ? { operationId:String(value.operationId).slice(0, 128) } : {}),
      ...(value.confirmed?.markdown ? { confirmed:{ markdown:String(value.confirmed.markdown) } } : {}),
    };
  }

  function durableAttachment(value) {
    const note = String(value.note || '').replace(/\s+/g, ' ').trim().slice(0, 240);
    return {
      filename: String(value.filename || ''),
      contentType: String(value.contentType || ''),
      ...(note ? { note } : {}),
      ...(value.operationId ? { operationId:String(value.operationId).slice(0, 128) } : {}),
      ...(value.confirmed?.markdown ? { confirmed:{ markdown:String(value.confirmed.markdown) } } : {}),
      ...(value.blob ? { blob:value.blob } : { data:String(value.data || '') }),
    };
  }

  function checklistOperation(value) {
    const action = String(value?.action || '');
    if (!['rename', 'remove', 'move-earlier', 'move-later'].includes(action)) return null;
    return {
      action,
      index:Number(value.index),
      ...(action === 'rename' ? { label:String(value.label || '') } : {}),
    };
  }

  function reviewFingerprint(message) {
    return JSON.stringify({
      body: String(message.body || ''),
      decision: String(message.decision || 'comment'),
      expectedHeadSha: String(message.expectedHeadSha || ''),
      comments: Array.isArray(message.comments) ? message.comments : [],
    });
  }

  function operationFingerprint(message) {
    return JSON.stringify({
      kind:String(message.kind || ''), repository:String(message.repository || ''),
      number:Number(message.number || 0), notificationId:Number(message.notificationId || 0),
      body:String(message.body || ''), targetKind:String(message.targetKind || ''),
      decision:String(message.decision || 'comment'), expectedHeadSha:String(message.expectedHeadSha || ''),
      comments:Array.isArray(message.comments) ? message.comments : [],
      blockerRepository:String(message.blockerRepository || ''), blockerNumber:Number(message.blockerNumber || 0),
      present:message.present === true, title:String(message.title || ''),
      expectedUpdatedAt:String(message.expectedUpdatedAt || ''),
      attachments:messageAttachments(message).map(attachmentMetadata),
    });
  }

  function clearConfirmedReviewState(item) {
    if (item.kind !== 'pull-review') return;
    try {
      if (item.draftKey && item.draftFingerprint &&
          storage?.getItem(item.draftKey) === item.draftFingerprint) storage.removeItem(item.draftKey);
      if (item.progressKey && item.progressFingerprint &&
          storage?.getItem(item.progressKey) === item.progressFingerprint) storage.removeItem(item.progressKey);
    } catch (_error) { /* Delivery is confirmed even when local cleanup is unavailable. */ }
  }

  function read() {
    try {
      const record = JSON.parse(storage?.getItem(storageKey) || 'null');
      if (![1, 2].includes(record?.version) || !Array.isArray(record.items)) return [];
      return record.items.filter(item => item && supportedKinds.has(item.kind));
    } catch (_error) { return []; }
  }

  function write(items, mirror = true) {
    storage?.setItem(storageKey, JSON.stringify({ version: 2, items }));
    coordinator?.notify('authored');
    if (mirror && backgroundSync?.reconcile) {
      Promise.resolve(backgroundSync.reconcile(items, 'authored'))
        .then(() => items.length ? backgroundSync.requestSync?.() : undefined)
        .catch(() => { /* Foreground reconnect remains the compatibility fallback. */ });
    }
  }

  function enqueue(message, mirror = true) {
    if (!supportedKinds.has(message?.kind)) throw new Error('This action cannot be queued.');
    if (message.kind === 'search-reply' && !['issue', 'pull'].includes(message.targetKind)) {
      throw new Error('Choose an exact Search result before queueing a reply.');
    }
    const ownerLogin = String(getOwnerLogin() || '').trim();
    if (!ownerLogin) throw new Error('Confirm your Gitea account before queueing a message.');
    const items = read();
    const requestedOperationId = String(message.operationId || '').slice(0, 128);
    const existing = requestedOperationId && items.find(item => item.operationId === requestedOperationId);
    if (existing) {
      if (operationFingerprint(existing) !== operationFingerprint(message)) {
        throw new Error('This operation ID is already bound to a different queued action.');
      }
      return { ...existing };
    }
    if (message.kind === 'pull-review') {
      const queuedReview = items.find(item => item.kind === 'pull-review' &&
        item.repository === String(message.repository || '') &&
        item.number === Number(message.number || 0) &&
        item.expectedHeadSha === String(message.expectedHeadSha || ''));
      if (queuedReview) {
        if (reviewFingerprint(queuedReview) === reviewFingerprint(message)) return { ...queuedReview };
        throw new Error('A review for this saved head is already queued. Open Drafts to inspect or discard it first.');
      }
    }
    if (message.kind === 'issue-content') {
      const queuedContent = items.find(item => item.kind === 'issue-content' && item.status === 'queued' &&
        item.ownerLogin === ownerLogin && item.repository === String(message.repository || '') &&
        item.number === Number(message.number || 0));
      if (queuedContent) {
        const operation = checklistOperation(message.checklistOperation);
        const replacement = {
          ...queuedContent,
          operationId: String(requestedOperationId || makeId()).slice(0, 128),
          title: String(message.title || ''),
          body: String(message.body || ''),
          ...(operation ? { checklistOperations:[...(queuedContent.checklistOperations || []), operation] } : {}),
        };
        write(items.map(item => item.id === queuedContent.id ? replacement : item), mirror);
        return replacement;
      }
    }
    if (items.length >= maxItems) throw new Error('Message outbox is full. Send or discard a queued message first.');
    const id = String(requestedOperationId || makeId()).slice(0, 128);
    const attachments = messageAttachments(message);
    const item = {
      id,
      operationId: requestedOperationId || id,
      kind: message.kind,
      repository: String(message.repository || ''),
      number: Number(message.number || 0),
      notificationId: Number(message.notificationId || 0),
      body: String(message.body || ''),
      ownerLogin,
      status: 'queued',
      queuedAt: Number(now()),
      ...(message.kind === 'search-reply' ? { targetKind:String(message.targetKind) } : {}),
      ...(message.kind === 'update-reply-read' ? { replyConfirmed: message.replyConfirmed === true } : {}),
      ...(['issue-comment', 'pull-comment', 'search-reply', 'update-reply', 'update-reply-read'].includes(message.kind) && attachments.length ?
        (attachments.length === 1 ? { attachment:attachmentMetadata(attachments[0]) } :
          { attachments:attachments.map(attachmentMetadata) }) : {}),
      ...(message.kind === 'pull-review' ? {
        decision: String(message.decision || 'comment'),
        expectedHeadSha: String(message.expectedHeadSha || ''),
        comments: Array.isArray(message.comments) ? message.comments.map(comment => ({ ...comment })) : [],
        draftKey: String(message.draftKey || ''),
        progressKey: String(message.progressKey || ''),
        draftFingerprint: String(message.draftFingerprint || ''),
        progressFingerprint: String(message.progressFingerprint || ''),
      } : {}),
      ...(message.kind === 'issue-blocker' ? {
        blockerRepository: String(message.blockerRepository || ''),
        blockerNumber: Number(message.blockerNumber || 0),
        present: message.present === true,
      } : {}),
      ...(message.kind === 'issue-content' ? {
        title: String(message.title || ''),
        baseBody: String(message.baseBody ?? message.body ?? ''),
        expectedUpdatedAt: String(message.expectedUpdatedAt || ''),
        ...(checklistOperation(message.checklistOperation) ? {
          checklistOperations:[checklistOperation(message.checklistOperation)],
        } : {}),
      } : {}),
    };
    items.push(item);
    write(items, mirror);
    return item;
  }

  async function enqueueDurably(message) {
    const previousItems = read();
    const item = enqueue(message, false);
    const attachments = messageAttachments(message);
    if (attachments.length && ['search-reply', 'update-reply', 'update-reply-read'].includes(message.kind) &&
        (!backgroundSync?.reconcile || !backgroundSync?.requestSync)) {
      write(read().filter(candidate => candidate.id !== item.id), false);
      throw new Error('Screenshot delivery needs IndexedDB. Your reply and screenshot are still here; retry.');
    }
    if (!backgroundSync?.reconcile || !backgroundSync?.requestSync) {
      return { item, background: false, durability: 'foreground-only' };
    }
    const durableItems = read().map(candidate => candidate.id === item.id && attachments.length ? {
      ...candidate,
      ...(attachments.length === 1 ? { attachment:durableAttachment(attachments[0]) } :
        { attachments:attachments.map(durableAttachment) }),
    } : candidate);
    try {
      await backgroundSync.reconcile(durableItems, 'authored');
    } catch (error) {
      write(previousItems, false);
      throw error;
    }
    try {
      await backgroundSync.requestSync();
      return { item, background: true, durability: 'background' };
    } catch (error) {
      return { item, background: false, durability: 'foreground-only', error };
    }
  }

  function update(id, changes) {
    let updated = null;
    write(read().map(item => {
      if (item.id !== id) return item;
      const body = String(changes?.body ?? item.body);
      updated = {
        ...item,
        body,
        operationId: body === item.body ? item.operationId : String(makeId()).slice(0, 128),
        status: 'queued',
      };
      delete updated.error;
      delete updated.deliveryState;
      return updated;
    }));
    return updated;
  }

  function discard(id) {
    const items = read();
    if (!items.some(item => item.id === id)) return false;
    write(items.filter(item => item.id !== id));
    return true;
  }

  function endpoint(item) {
    if (item.kind === 'update-reply') {
      return 'api/v1/notifications/' + encodeURIComponent(item.notificationId) + '/reply';
    }
    const repository = item.repository.split('/').map(encodeURIComponent).join('/');
    if (item.kind === 'issue-close') {
      return 'api/v1/repos/' + repository + '/issues/' + encodeURIComponent(item.number) + '/close';
    }
    if (item.kind === 'issue-blocker') {
      return 'api/v1/repos/' + repository + '/issues/' + encodeURIComponent(item.number) + '/blockers';
    }
    if (item.kind === 'issue-content') {
      return 'api/v1/repos/' + repository + '/issues/' + encodeURIComponent(item.number) + '/content';
    }
    if (item.kind === 'pull-review') {
      return 'api/v1/repos/' + repository + '/pulls/' + encodeURIComponent(item.number) + '/review';
    }
    if (item.kind === 'search-reply') {
      return 'api/v1/repos/' + repository + '/issues/' + encodeURIComponent(item.number) +
        '/preview/comments?kind=' + encodeURIComponent(item.targetKind);
    }
    const resource = item.kind === 'pull-comment' ? 'pulls' : 'issues';
    return 'api/v1/repos/' + repository + '/' + resource + '/' + encodeURIComponent(item.number) + '/comments';
  }

  async function sendItem(item, currentLogin) {
    if (!currentLogin || item.ownerLogin !== currentLogin) return { blocked: true };
    if (pending.has(item.id)) return pending.get(item.id);
    const attemptAt = Number(now());
    write(read().map(candidate => candidate.id === item.id ? {
      ...candidate, status:'sending', lastAttemptAt:attemptAt,
    } : candidate), false);
    const request = (async () => {
      try {
        let result;
        if (backgroundSync?.send) {
          const delivery = await backgroundSync.send(item, currentLogin);
          if (delivery.attention) {
            const error = delivery.error || new Error('Message needs attention');
            error.status = Number(error.status || 422);
            throw error;
          }
          result = delivery.message;
        } else {
          if (item.kind === 'update-reply-read') {
            if (!item.replyConfirmed) {
              await fetchJson('api/v1/notifications/' + encodeURIComponent(item.notificationId) + '/reply', {
                method: 'POST',
                headers: {
                  Accept: 'application/json', 'Content-Type': 'application/json',
                  'Idempotency-Key': item.operationId,
                },
                body: JSON.stringify({ body: item.body }),
              });
              write(read().map(candidate => candidate.id === item.id ? {
                ...candidate, replyConfirmed: true, status: 'sending', lastAttemptAt: attemptAt,
              } : candidate), false);
            }
            result = await fetchJson('api/v1/notifications/' + encodeURIComponent(item.notificationId) + '/read', {
              method: 'PATCH', headers: { Accept: 'application/json' },
            });
          } else if (item.kind === 'issue-close') {
            result = await fetchJson(endpoint(item), {
              method: 'PATCH',
              headers: { Accept: 'application/json', 'Idempotency-Key': item.operationId },
            });
            if (result?.state !== 'closed') throw new Error('Issue closure was not confirmed.');
          } else if (item.kind === 'issue-blocker') {
            result = await fetchJson(endpoint(item), {
              method: 'PATCH',
              headers: {
                Accept: 'application/json',
                'Content-Type': 'application/json',
                'Idempotency-Key': item.operationId,
              },
              body: JSON.stringify({
                repository: item.blockerRepository,
                number: item.blockerNumber,
                present: item.present,
              }),
            });
            const dependencies = Array.isArray(result?.dependencies) ? result.dependencies : [];
            const present = dependencies.some(candidate => candidate?.repository === item.blockerRepository &&
              Number(candidate?.number) === item.blockerNumber);
            if (result?.number !== item.number || result?.dependencies_available !== true || present !== item.present) {
              throw new Error('Blocker change was not confirmed.');
            }
          } else {
            const body = item.kind === 'pull-review' ? {
              body: item.body,
              decision: item.decision,
              expected_head_sha: item.expectedHeadSha,
              comments: item.comments,
            } : item.kind === 'issue-content' ? {
              title:item.title, body:item.body, expected_updated_at:item.expectedUpdatedAt,
            } : { body: item.body };
            result = await fetchJson(endpoint(item), {
              method: item.kind === 'issue-content' ? 'PATCH' : 'POST',
              headers: {
                Accept: 'application/json',
                'Content-Type': 'application/json',
                'Idempotency-Key': item.operationId,
              },
              body: JSON.stringify(body),
            });
            if (item.kind === 'issue-content' &&
                (result?.number !== item.number || result?.title !== item.title || result?.body !== item.body)) {
              throw new Error('Checklist update was not confirmed.');
            }
          }
        }
        if (!result) return { blocked: true };
        clearConfirmedReviewState(item);
        discard(item.id);
        return { result };
      } catch (error) {
        const status = Number(error?.status || 0);
        const permanent = status >= 400 && status < 500;
        const attemptError = String(error.message || 'Delivery failed').slice(0, 240);
        if (permanent) {
          write(read().map(candidate => candidate.id === item.id ? {
            ...candidate,
            status: 'attention',
            error: String(error.message || 'Message needs attention').slice(0, 240),
            lastAttemptAt: attemptAt,
            lastAttemptError: attemptError,
            ...(error.code === 'delivery_uncertain' ? { deliveryState: 'uncertain' } : {}),
          } : candidate));
        } else {
          write(read().map(candidate => candidate.id === item.id ? {
            ...candidate, status:'queued', lastAttemptAt:attemptAt, lastAttemptError:attemptError,
          } : candidate));
        }
        return { error, transient: !permanent };
      }
    })();
    pending.set(item.id, request);
    try { return await request; }
    finally { if (pending.get(item.id) === request) pending.delete(item.id); }
  }

  async function flushQueue(currentLogin) {
    const confirmed = [];
    let blocked = 0;
    currentLogin = String(currentLogin || '').trim();
    for (const item of read()) {
      if (item.status === 'attention' || item.kind === 'issue-close') continue;
      if (!currentLogin || item.ownerLogin !== currentLogin) { blocked += 1; continue; }
      const outcome = await sendItem(item, currentLogin);
      if (outcome.result) confirmed.push(outcome.result);
      if (outcome.transient) break;
    }
    return { confirmed, remaining: read(), blocked };
  }

  async function flush(currentLogin) {
    if (coordinator) return coordinator.runExclusive('authored', () => flushQueue(currentLogin));
    return flushQueue(currentLogin);
  }

  async function retryItem(id, currentLogin) {
    const item = read().find(candidate => candidate.id === id);
    if (!item) return { confirmed: [], remaining: read() };
    currentLogin = String(currentLogin || '').trim();
    if (!currentLogin || item.ownerLogin !== currentLogin) {
      return { confirmed: [], remaining: read(), blocked: 1 };
    }
    let queued = item;
    if (item.status === 'attention') {
      queued = {
        ...item,
        operationId: String(makeId()).slice(0, 128),
        status: 'queued',
      };
      delete queued.error;
      delete queued.deliveryState;
      write(read().map(candidate => candidate.id === id ? queued : candidate));
    }
    const outcome = await sendItem(queued, currentLogin);
    return { confirmed: outcome.result ? [outcome.result] : [], remaining: read(), blocked: 0 };
  }

  async function retry(id, currentLogin) {
    if (coordinator) return coordinator.runExclusive('authored', () => retryItem(id, currentLogin));
    return retryItem(id, currentLogin);
  }

  async function resolveIssueContentConflict(id, latest, currentLogin) {
    const previousItems = read();
    const item = previousItems.find(candidate => candidate.id === id);
    currentLogin = String(currentLogin || '').trim();
    if (!item || item.kind !== 'issue-content' || item.status !== 'attention') {
      throw new Error('This checklist conflict is no longer available.');
    }
    if (!currentLogin || item.ownerLogin !== currentLogin) {
      throw new Error('Confirm the account that queued this checklist update.');
    }
    if (typeof mergeChecklistConflict !== 'function') {
      throw new Error('Checklist conflict review is unavailable.');
    }
    const merged = mergeChecklistConflict({
      baseBody: String(item.baseBody ?? item.body ?? ''),
      localBody: String(item.body || ''),
      remoteBody: String(latest?.body || ''),
    });
    if (merged.conflicts?.length) return merged;
    if (!String(latest?.updated_at || '')) throw new Error('Latest issue revision is unavailable.');
    const rebased = {
      ...item,
      operationId: String(makeId()).slice(0, 128),
      title: String(latest?.title || ''),
      baseBody: String(latest?.body || ''),
      body: merged.body,
      expectedUpdatedAt: String(latest.updated_at),
      status: 'queued',
    };
    delete rebased.error;
    delete rebased.deliveryState;
    const nextItems = previousItems.map(candidate => candidate.id === id ? rebased : candidate);
    write(nextItems, false);
    if (backgroundSync?.reconcile) {
      try { await backgroundSync.reconcile(nextItems, 'authored'); }
      catch (error) {
        write(previousItems, false);
        throw error;
      }
      try { await backgroundSync.requestSync?.(); }
      catch (_error) { /* Foreground retry remains available. */ }
    }
    return { ...merged, item: rebased };
  }

  function reconcileBackground(records) {
    const statuses = new Map((records || []).map(item => [item.id, item]));
    const items = read().flatMap(item => {
      const background = statuses.get(item.id);
      if (background?.status === 'sent') {
        clearConfirmedReviewState(item);
        return [];
      }
      if (background?.status === 'attention') return [{
        ...item,
        status: 'attention',
        error: String(background.error || 'Message needs attention').slice(0, 240),
        ...(background.deliveryState ? { deliveryState: background.deliveryState } : {}),
      }];
      if (background?.status === 'authorization') return [{
        ...item,
        status: 'authorization',
        error: String(background.error || 'Fresh authorization required').slice(0, 240),
      }];
      return [item];
    });
    write(items);
    return items;
  }

  return { enqueue, enqueueDurably, update, discard, flush, retry, resolveIssueContentConflict,
    reconcileBackground, list: () => read().map(item => ({ ...item })) };
}

if (typeof module !== 'undefined' && module.exports) module.exports = createAuthoredOutbox;
