function createUpdateOwnership({
  claim, addToday, available, start, recover, onClaimed, onState, onStartState,
}) {
  let detail = null;
  let update = null;
  let claimedItem = null;
  let request = null;

  function state(value) {
    onState?.(value);
  }

  function startState(value) {
    onStartState?.(value);
  }

  function claimed(result) {
    claimedItem = {
      ...result,
      repository: detail.repository,
      kind: 'issue',
      notification_id: update?.notification_id,
      updated_at: update?.updated_at || result.updated_at,
      has_update: true,
    };
    detail.issue.claimable = false;
    onClaimed?.(claimedItem);
    startState({ action: 'hidden', busy: false, message: '' });
    return claimedItem;
  }

  function recoverStartedItem(item) {
    recover?.(item);
    startState({
      action: 'hidden',
      busy: false,
      message: 'Assigned to you, but Today could not start. The update is still unread; the owned issue is open so you can recover.',
    });
    state({ action: 'today', busy: false, message: '' });
    return 'recovery';
  }

  function open(nextDetail, nextUpdate) {
    detail = nextDetail;
    update = nextUpdate;
    claimedItem = null;
    request = null;
    state({
      action: nextDetail?.issue?.claimable ? 'claim' : 'hidden',
      busy: false,
      message: '',
    });
    startState({
      action: nextDetail?.issue?.claimable && available && start ? 'start' : 'hidden',
      busy: false,
      message: '',
    });
  }

  function act() {
    if (request) return request;
    if (claimedItem) {
      const result = addToday(claimedItem);
      const messages = {
        added: 'Added to Today.',
        exists: 'This issue is already in Today.',
        full: 'Today is limited to 5 items. Remove one before adding more.',
        unavailable: 'Could not save Today on this device.',
      };
      state({ action: 'today', busy: false, message: messages[result] || messages.unavailable });
      return Promise.resolve(result);
    }
    if (!detail?.issue?.claimable) return Promise.resolve('hidden');

    state({ action: 'claim', busy: true, message: 'Assigning…' });
    request = Promise.resolve(claim({
      repository: detail.repository,
      number: detail.issue.number,
    })).then(result => {
      claimed(result);
      state({ action: 'today', busy: false, message: 'Assigned to you. The update is still unread.' });
      return claimedItem;
    }).catch(error => {
      if (error?.status === 409) {
        detail.issue.claimable = false;
        startState({ action: 'hidden', busy: false, message: '' });
        state({
          action: 'hidden',
          busy: false,
          message: 'Someone else claimed or closed this issue. The update is still unread.',
        });
        return 'conflict';
      }
      state({
        action: 'claim',
        busy: false,
        message: (error?.message || 'The issue could not be assigned.') + ' The update and your reply draft are safe; retry.',
      });
      return 'retry';
    }).finally(() => { request = null; });
    return request;
  }

  function startNow() {
    if (request) return request;
    if (!detail?.issue?.claimable || !available || !start) return Promise.resolve('hidden');
    if (!available()) {
      const message = 'Today is limited to 5 items. Remove one before taking ownership.';
      startState({ action: 'start', busy: false, message });
      return Promise.resolve('full');
    }
    startState({ action: 'start', busy: true, message: 'Assigning and starting…' });
    state({ action: 'claim', busy: true, message: '' });
    request = Promise.resolve(claim({
      repository: detail.repository,
      number: detail.issue.number,
    })).then(result => {
      const item = claimed(result);
      const outcome = start(item);
      if (outcome === 'started') {
        startState({
          action: 'hidden',
          busy: false,
          message: 'Assigned, added to Today, and ready to work. The update is still unread.',
        });
        state({ action: 'today', busy: false, message: '' });
        return outcome;
      }
      return recoverStartedItem(item);
    }).catch(error => {
      if (claimedItem) {
        return recoverStartedItem(claimedItem);
      }
      if (error?.status === 409) {
        detail.issue.claimable = false;
        state({ action: 'hidden', busy: false, message: '' });
        startState({
          action: 'hidden',
          busy: false,
          message: 'Someone else claimed or closed this issue. The update is still unread.',
        });
        return 'conflict';
      }
      state({ action: 'claim', busy: false, message: '' });
      startState({
        action: 'start',
        busy: false,
        message: (error?.message || 'The issue could not be assigned.') + ' The update and your reply draft are safe; retry.',
      });
      return 'retry';
    }).finally(() => { request = null; });
    return request;
  }

  return { open, act, start:startNow };
}

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