(function attachBackgroundContributionOAuth(root, factory) {
  root.MultiPageBackgroundContributionOAuth = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundContributionOAuthModule() {
  const API_BASE_URL = 'https://apikey.qzz.io/oauth/api';
  const ACTIVE_STATUSES = new Set(['started', 'waiting', 'processing']);
  const FINAL_STATUSES = new Set(['auto_approved', 'auto_rejected', 'manual_review_required', 'expired', 'error']);
  const CALLBACK_FINAL_STATUSES = new Set(['submitted']);
  const CALLBACK_WAITING_STATUSES = new Set(['idle', 'waiting', 'captured', 'failed', 'submitting']);

  const RUNTIME_DEFAULTS = {
    contributionMode: false,
    contributionModeExpected: false,
    contributionSource: 'sub2api',
    contributionTargetGroupName: 'codex号池',
    contributionNickname: '',
    contributionQq: '',
    contributionSessionId: '',
    contributionAuthUrl: '',
    contributionAuthState: '',
    contributionCallbackUrl: '',
    contributionStatus: '',
    contributionStatusMessage: '',
    contributionLastPollAt: 0,
    contributionCallbackStatus: 'idle',
    contributionCallbackMessage: '',
    contributionAuthOpenedAt: 0,
    contributionAuthTabId: 0,
  };

  const RUNTIME_KEYS = Object.keys(RUNTIME_DEFAULTS);

  function createContributionOAuthManager(deps = {}) {
    const {
      addLog,
      broadcastDataUpdate,
      chrome,
      closeLocalhostCallbackTabs,
      getState,
      setState,
    } = deps;

    let listenersBound = false;
    const pendingCallbackSubmissions = new Map();
    const pendingCapturedCallbacks = new Map();

    function normalizeString(value = '') {
      return String(value || '').trim();
    }

    function normalizePositiveInteger(value, fallback = 0) {
      const numeric = Math.floor(Number(value) || 0);
      return numeric > 0 ? numeric : fallback;
    }

    function normalizeContributionStatus(value = '') {
      const normalized = normalizeString(value).toLowerCase();
      switch (normalized) {
        case 'started':
          return 'started';
        case 'waiting':
        case 'wait':
          return 'waiting';
        case 'processing':
          return 'processing';
        case 'auto_approved':
        case 'approved':
          return 'auto_approved';
        case 'auto_rejected':
        case 'rejected':
          return 'auto_rejected';
        case 'manual_review_required':
        case 'manual_review':
          return 'manual_review_required';
        case 'expired':
        case 'timeout':
          return 'expired';
        case 'error':
        case 'failed':
          return 'error';
        default:
          return '';
      }
    }

    function normalizeContributionCallbackStatus(value = '') {
      const normalized = normalizeString(value).toLowerCase();
      switch (normalized) {
        case 'idle':
          return 'idle';
        case 'waiting':
        case 'pending':
          return 'waiting';
        case 'captured':
          return 'captured';
        case 'submitting':
        case 'processing':
          return 'submitting';
        case 'submitted':
        case 'success':
        case 'done':
          return 'submitted';
        case 'failed':
        case 'error':
          return 'failed';
        default:
          return '';
      }
    }

    function isContributionFinalStatus(status = '') {
      return FINAL_STATUSES.has(normalizeContributionStatus(status));
    }

    function getStatusLabel(status = '') {
      switch (normalizeContributionStatus(status)) {
        case 'started':
          return '已生成登录地址';
        case 'waiting':
          return '等待提交回调';
        case 'processing':
          return '已提交回调，等待 CPA 确认';
        case 'auto_approved':
          return '贡献成功，CPA 已确认';
        case 'auto_rejected':
          return '贡献未通过确认';
        case 'manual_review_required':
          return '已提交，等待人工处理';
        case 'expired':
          return '贡献会话已超时';
        case 'error':
          return '贡献流程失败';
        default:
          return '等待开始贡献';
      }
    }

    function getCallbackLabel(status = '') {
      switch (normalizeContributionCallbackStatus(status)) {
        case 'waiting':
        case 'idle':
          return '等待回调';
        case 'captured':
          return '已捕获回调地址';
        case 'submitting':
          return '正在提交回调';
        case 'submitted':
          return '已提交回调';
        case 'failed':
          return '回调提交失败';
        default:
          return '等待回调';
      }
    }

    function unwrapPayload(payload) {
      if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
        return {};
      }

      if (payload.data && typeof payload.data === 'object' && !Array.isArray(payload.data)) {
        return { ...payload.data, ...payload };
      }

      return payload;
    }

    function getErrorMessage(payload, responseStatus = 500) {
      const details = [
        payload?.message,
        payload?.detail,
        payload?.error,
        payload?.reason,
      ]
        .map((item) => normalizeString(item))
        .find(Boolean);

      if (details) {
        return details;
      }

      return `贡献服务请求失败（HTTP ${responseStatus}）。`;
    }

    async function fetchContributionJson(endpoint, options = {}) {
      const controller = new AbortController();
      const timeoutMs = Math.max(1000, Math.floor(Number(options.timeoutMs) || 15000));
      const timer = setTimeout(() => controller.abort(), timeoutMs);

      try {
        const response = await fetch(`${API_BASE_URL}${endpoint}`, {
          method: options.method || 'GET',
          headers: {
            Accept: 'application/json',
            ...(options.body ? { 'Content-Type': 'application/json' } : {}),
          },
          body: options.body ? JSON.stringify(options.body) : undefined,
          signal: controller.signal,
        });

        let rawPayload = {};
        try {
          rawPayload = await response.json();
        } catch {
          rawPayload = {};
        }

        const payload = unwrapPayload(rawPayload);
        if (!response.ok || payload.ok === false) {
          const error = new Error(getErrorMessage(payload, response.status));
          error.payload = payload;
          error.responseStatus = response.status;
          throw error;
        }

        return payload;
      } catch (error) {
        if (error?.name === 'AbortError') {
          throw new Error('贡献服务请求超时，请稍后重试。');
        }
        throw error;
      } finally {
        clearTimeout(timer);
      }
    }

    function pickContributionState(state = {}) {
      const picked = {};
      for (const key of RUNTIME_KEYS) {
        picked[key] = state[key] !== undefined ? state[key] : RUNTIME_DEFAULTS[key];
      }
      return picked;
    }

    async function applyRuntimeUpdates(updates = {}) {
      if (!updates || typeof updates !== 'object' || Array.isArray(updates) || Object.keys(updates).length === 0) {
        return getState();
      }

      await setState(updates);
      broadcastDataUpdate(updates);
      return getState();
    }

    function extractAuthStateFromUrl(authUrl = '') {
      try {
        return new URL(authUrl).searchParams.get('state') || '';
      } catch {
        return '';
      }
    }

    function buildNickname(state = {}, preferredNickname = '') {
      const nickname = normalizeString(preferredNickname)
        || normalizeString(state.contributionNickname);
      return nickname || '';
    }

    function buildContributionQq(state = {}, preferredQq = '') {
      const qq = normalizeString(preferredQq) || normalizeString(state.contributionQq);
      return qq;
    }

    function isPlusModeState(state = {}) {
      return Boolean(state?.plusModeEnabled);
    }

    function normalizeContributionModeSource(value = '') {
      const normalized = normalizeString(value).toLowerCase();
      return normalized === 'sub2api' ? 'sub2api' : 'cpa';
    }

    function resolveContributionModeRoutingState(state = {}) {
      const currentStatus = normalizeString(state?.contributionStatus).toLowerCase();
      const currentSource = normalizeContributionModeSource(state?.contributionSource);
      const hasActiveSession = Boolean(
        normalizeString(state?.contributionSessionId)
        && currentStatus
        && !FINAL_STATUSES.has(currentStatus)
      );

      if (hasActiveSession) {
        return {
          source: currentSource,
          targetGroupName: currentSource === 'sub2api'
            ? (normalizeString(state?.contributionTargetGroupName) || 'codex号池')
            : '',
        };
      }

      return {
        source: 'sub2api',
        targetGroupName: isPlusModeState(state)
          ? 'openai-plus'
          : (normalizeString(state?.contributionTargetGroupName) || 'codex号池'),
      };
    }

    function buildStatusMessage(status, payload = {}) {
      const label = getStatusLabel(status);
      const details = [
        payload.status_message,
        payload.statusMessage,
        payload.message,
        payload.detail,
        payload.reason,
      ]
        .map((item) => normalizeString(item))
        .find(Boolean);

      if (!details || details === label) {
        return label;
      }

      return `${label}：${details}`;
    }

    function buildCallbackMessage(status, payload = {}) {
      const label = getCallbackLabel(status);
      const details = [
        payload.callback_message,
        payload.callbackMessage,
        payload.message,
        payload.detail,
        payload.reason,
      ]
        .map((item) => normalizeString(item))
        .find(Boolean);

      if (!details || details === label) {
        return label;
      }

      return `${label}：${details}`;
    }

    function deriveCallbackState(payload = {}, state = {}) {
      const existingStatus = normalizeContributionCallbackStatus(state.contributionCallbackStatus);
      const callbackUrl = normalizeString(
        payload.callback_url
        || payload.callbackUrl
        || state.contributionCallbackUrl
      );
      const explicitStatus = normalizeContributionCallbackStatus(
        payload.callback_status
        || payload.callbackStatus
      );

      if (explicitStatus) {
        return {
          status: explicitStatus,
          message: buildCallbackMessage(explicitStatus, payload),
          callbackUrl,
        };
      }

      if (payload.callback_submitted === true || payload.callbackSubmitted === true) {
        return {
          status: 'submitted',
          message: buildCallbackMessage('submitted', payload),
          callbackUrl,
        };
      }

      if (callbackUrl) {
        return {
          status: CALLBACK_FINAL_STATUSES.has(existingStatus) ? existingStatus : 'captured',
          message: buildCallbackMessage(CALLBACK_FINAL_STATUSES.has(existingStatus) ? existingStatus : 'captured', payload),
          callbackUrl,
        };
      }

      if (CALLBACK_FINAL_STATUSES.has(existingStatus) || existingStatus === 'failed') {
        return {
          status: existingStatus,
          message: normalizeString(state.contributionCallbackMessage) || buildCallbackMessage(existingStatus),
          callbackUrl: normalizeString(state.contributionCallbackUrl),
        };
      }

      return {
        status: 'waiting',
        message: buildCallbackMessage('waiting', payload),
        callbackUrl: '',
      };
    }

    function isContributionCallbackUrl(rawUrl, state = {}) {
      const urlText = normalizeString(rawUrl);
      if (!urlText) {
        return false;
      }

      let parsed;
      try {
        parsed = new URL(urlText);
      } catch {
        return false;
      }

      if (!['http:', 'https:'].includes(parsed.protocol)) {
        return false;
      }

      const code = normalizeString(parsed.searchParams.get('code'));
      const errorText = normalizeString(parsed.searchParams.get('error'))
        || normalizeString(parsed.searchParams.get('error_description'));
      const authState = normalizeString(parsed.searchParams.get('state'));
      if ((!code && !errorText) || !authState) {
        return false;
      }

      const hostLooksLocal = ['localhost', '127.0.0.1'].includes(parsed.hostname);
      const pathLooksLikeCallback = /callback/i.test(parsed.pathname || '');
      if (!hostLooksLocal && !pathLooksLikeCallback) {
        return false;
      }

      const expectedState = normalizeString(state.contributionAuthState);
      return !expectedState || expectedState === authState;
    }

    async function openContributionAuthUrl(authUrl, options = {}) {
      const normalizedUrl = normalizeString(authUrl);
      if (!normalizedUrl) {
        throw new Error('贡献服务未返回有效的登录地址。');
      }

      const currentState = options.stateOverride || await getState();
      const preferredTabId = normalizePositiveInteger(options.tabId || currentState.contributionAuthTabId, 0);
      let tab = null;

      if (preferredTabId) {
        tab = await chrome.tabs.update(preferredTabId, {
          url: normalizedUrl,
          active: true,
        }).catch(() => null);
      }

      if (!tab) {
        tab = await chrome.tabs.create({ url: normalizedUrl, active: true });
      }

      await applyRuntimeUpdates({
        contributionAuthUrl: normalizedUrl,
        contributionAuthOpenedAt: Date.now(),
        contributionAuthTabId: normalizePositiveInteger(tab?.id, 0),
      });

      return tab;
    }

    async function fetchContributionResult(sessionId) {
      try {
        return await fetchContributionJson(`/result?session_id=${encodeURIComponent(sessionId)}`);
      } catch (error) {
        if (typeof addLog === 'function') {
          await addLog(`贡献模式：获取最终结果失败：${error.message}`, 'warn');
        }
        return null;
      }
    }

    async function submitContributionCallback(callbackUrl, options = {}) {
      const currentState = options.stateOverride || await getState();
      const sessionId = normalizeString(currentState.contributionSessionId);
      const normalizedUrl = normalizeString(callbackUrl);

      if (!sessionId || !normalizedUrl) {
        return currentState;
      }

      const currentCallbackStatus = normalizeContributionCallbackStatus(currentState.contributionCallbackStatus);
      if (CALLBACK_FINAL_STATUSES.has(currentCallbackStatus) || currentCallbackStatus === 'submitting') {
        return currentState;
      }

      const dedupeKey = `${sessionId}::${normalizedUrl}`;
      if (pendingCallbackSubmissions.has(dedupeKey)) {
        return pendingCallbackSubmissions.get(dedupeKey);
      }

      const task = (async () => {
        await applyRuntimeUpdates({
          contributionCallbackUrl: normalizedUrl,
          contributionCallbackStatus: 'submitting',
          contributionCallbackMessage: buildCallbackMessage('submitting'),
        });

        try {
          const payload = await fetchContributionJson('/submit-callback', {
            method: 'POST',
            body: {
              session_id: sessionId,
              callback_url: normalizedUrl,
            },
          });

          const nextStatus = 'submitted';
          await applyRuntimeUpdates({
            contributionCallbackUrl: normalizedUrl,
            contributionCallbackStatus: nextStatus,
            contributionCallbackMessage: buildCallbackMessage(nextStatus, payload),
          });

          if (typeof closeLocalhostCallbackTabs === 'function') {
            await closeLocalhostCallbackTabs(normalizedUrl).catch(() => {});
          }

          return await pollContributionStatus({ reason: options.reason || 'submit_callback' });
        } catch (error) {
          await applyRuntimeUpdates({
            contributionCallbackUrl: normalizedUrl,
            contributionCallbackStatus: 'failed',
            contributionCallbackMessage: `回调提交失败：${error.message}`,
          });

          if (typeof addLog === 'function') {
            await addLog(`贡献模式：回调提交失败：${error.message}`, 'warn');
          }

          throw error;
        } finally {
          pendingCallbackSubmissions.delete(dedupeKey);
        }
      })();

      pendingCallbackSubmissions.set(dedupeKey, task);
      return task;
    }

    async function handleCapturedCallback(rawUrl, metadata = {}) {
      const currentState = await getState();
      if (!normalizeString(currentState.contributionSessionId) || !currentState.contributionMode) {
        return currentState;
      }
      if (!isContributionCallbackUrl(rawUrl, currentState)) {
        return currentState;
      }

      const normalizedUrl = normalizeString(rawUrl);
      const callbackDedupeKey = `${normalizeString(currentState.contributionSessionId)}::${normalizedUrl}`;
      if (pendingCapturedCallbacks.has(callbackDedupeKey)) {
        return pendingCapturedCallbacks.get(callbackDedupeKey);
      }
      if (pendingCallbackSubmissions.has(callbackDedupeKey)) {
        return pendingCallbackSubmissions.get(callbackDedupeKey);
      }
      const currentCallbackStatus = normalizeContributionCallbackStatus(currentState.contributionCallbackStatus);
      if (
        normalizedUrl
        && normalizeString(currentState.contributionCallbackUrl) === normalizedUrl
        && (CALLBACK_FINAL_STATUSES.has(currentCallbackStatus) || currentCallbackStatus === 'submitting')
      ) {
        return currentState;
      }

      const task = (async () => {
        await applyRuntimeUpdates({
          contributionCallbackUrl: normalizedUrl,
          contributionCallbackStatus: 'captured',
          contributionCallbackMessage: buildCallbackMessage('captured'),
        });

        if (typeof addLog === 'function') {
          await addLog(`贡献模式：已捕获回调地址（${metadata.source || 'unknown'}）。`, 'info');
        }

        try {
          return await submitContributionCallback(normalizedUrl, {
            reason: metadata.source || 'navigation',
            stateOverride: await getState(),
          });
        } catch {
          return getState();
        } finally {
          pendingCapturedCallbacks.delete(callbackDedupeKey);
        }
      })();

      pendingCapturedCallbacks.set(callbackDedupeKey, task);
      return task;
    }

    async function pollContributionStatus(options = {}) {
      const currentState = options.stateOverride || await getState();
      const sessionId = normalizeString(currentState.contributionSessionId);
      if (!sessionId) {
        return currentState;
      }

      const payload = await fetchContributionJson(`/status?session_id=${encodeURIComponent(sessionId)}`);
      const nextStatus = normalizeContributionStatus(payload.status || payload.state || payload.phase) || currentState.contributionStatus || 'waiting';
      let finalPayload = null;

      if (isContributionFinalStatus(nextStatus)) {
        finalPayload = await fetchContributionResult(sessionId);
      }

      const mergedPayload = finalPayload ? { ...payload, ...finalPayload } : payload;
      const normalizedStatus = normalizeContributionStatus(mergedPayload.status || mergedPayload.state || mergedPayload.phase) || nextStatus;
      const callbackState = deriveCallbackState(mergedPayload, currentState);
      const updates = {
        contributionLastPollAt: Date.now(),
        contributionStatus: normalizedStatus,
        contributionStatusMessage: buildStatusMessage(normalizedStatus, mergedPayload),
        contributionCallbackUrl: callbackState.callbackUrl,
        contributionCallbackStatus: callbackState.status,
        contributionCallbackMessage: callbackState.message,
      };

      const authUrl = normalizeString(mergedPayload.auth_url || mergedPayload.authUrl);
      if (authUrl) {
        updates.contributionAuthUrl = authUrl;
      }

      const authState = normalizeString(mergedPayload.state || mergedPayload.auth_state || mergedPayload.authState)
        || (authUrl ? extractAuthStateFromUrl(authUrl) : '');
      if (authState) {
        updates.contributionAuthState = authState;
      }

      await applyRuntimeUpdates(updates);
      const nextState = await getState();

      if (
        normalizeString(nextState.contributionCallbackUrl)
        && CALLBACK_WAITING_STATUSES.has(normalizeContributionCallbackStatus(nextState.contributionCallbackStatus))
      ) {
        try {
          return await submitContributionCallback(nextState.contributionCallbackUrl, {
            reason: options.reason || 'status_poll',
            stateOverride: nextState,
          });
        } catch {
          return getState();
        }
      }

      return nextState;
    }

    async function startContributionFlow(options = {}) {
      const currentState = options.stateOverride || await getState();
      const shouldOpenAuthTab = options.openAuthTab !== false;
      if (!currentState.contributionMode) {
        throw new Error('请先进入贡献模式。');
      }

      const currentSessionId = normalizeString(currentState.contributionSessionId);
      const currentStatus = normalizeContributionStatus(currentState.contributionStatus);
      if (currentSessionId && ACTIVE_STATUSES.has(currentStatus)) {
        if (normalizeString(currentState.contributionAuthUrl)) {
          if (shouldOpenAuthTab) {
            await openContributionAuthUrl(currentState.contributionAuthUrl, {
              stateOverride: currentState,
            }).catch(() => null);
          }
        }
        return pollContributionStatus({ reason: 'resume_existing' });
      }

      const routingState = resolveContributionModeRoutingState(currentState);
      const payload = await fetchContributionJson('/start', {
        method: 'POST',
        body: {
          nickname: buildNickname(currentState, options.nickname),
          qq: buildContributionQq(currentState, options.qq),
          email: normalizeString(currentState.email),
          source: routingState.source,
          target_group_name: routingState.targetGroupName,
          channel: 'codex-extension',
        },
      });

      const sessionId = normalizeString(payload.session_id || payload.sessionId);
      const authUrl = normalizeString(payload.auth_url || payload.authUrl);
      const authState = normalizeString(payload.state || payload.auth_state || payload.authState) || extractAuthStateFromUrl(authUrl);
      if (!sessionId || !authUrl) {
        throw new Error('贡献服务未返回有效的 session_id 或 auth_url。');
      }

      await applyRuntimeUpdates({
        contributionSessionId: sessionId,
        contributionAuthUrl: authUrl,
        contributionAuthState: authState,
        contributionCallbackUrl: '',
        contributionStatus: normalizeContributionStatus(payload.status) || 'started',
        contributionStatusMessage: buildStatusMessage(normalizeContributionStatus(payload.status) || 'started', payload),
        contributionLastPollAt: 0,
        contributionCallbackStatus: 'waiting',
        contributionCallbackMessage: buildCallbackMessage('waiting'),
        contributionAuthOpenedAt: 0,
        contributionAuthTabId: 0,
      });

      if (shouldOpenAuthTab) {
        await openContributionAuthUrl(authUrl);
      }
      return pollContributionStatus({ reason: 'after_start' });
    }

    function onNavigationEvent(details = {}, source) {
      if (details?.frameId !== undefined && Number(details.frameId) !== 0) {
        return;
      }
      handleCapturedCallback(details?.url || '', {
        source,
        tabId: normalizePositiveInteger(details?.tabId, 0),
      }).catch(() => {});
    }

    function onTabUpdated(tabId, changeInfo, tab) {
      const candidateUrl = normalizeString(changeInfo?.url || tab?.url);
      if (!candidateUrl) {
        return;
      }
      handleCapturedCallback(candidateUrl, {
        source: 'tabs.onUpdated',
        tabId: normalizePositiveInteger(tabId, 0),
      }).catch(() => {});
    }

    function ensureCallbackListeners() {
      if (listenersBound) {
        return;
      }

      chrome.webNavigation.onCommitted.addListener((details) => {
        onNavigationEvent(details, 'webNavigation.onCommitted');
      });
      chrome.webNavigation.onHistoryStateUpdated.addListener((details) => {
        onNavigationEvent(details, 'webNavigation.onHistoryStateUpdated');
      });
      chrome.tabs.onUpdated.addListener(onTabUpdated);
      listenersBound = true;
    }

    return {
      ensureCallbackListeners,
      handleCapturedCallback,
      isContributionCallbackUrl,
      isContributionFinalStatus,
      pollContributionStatus,
      startContributionFlow,
      submitContributionCallback,
    };
  }

  return {
    ACTIVE_STATUSES,
    FINAL_STATUSES,
    RUNTIME_DEFAULTS,
    RUNTIME_KEYS,
    createContributionOAuthManager,
  };
});
