(() => {
  if (window.__pulsePersonalCollectorPatched__) return;
  window.__pulsePersonalCollectorPatched__ = true;

  const PERSONAL_INTERVAL_MS = 15 * 60 * 1000;
  const PERSONAL_RETRY_MS = 60 * 1000;
  const LOG_PREFIX = '[pulse-personal]';
  let timer = null;
  let running = false;

  function log(...args) {
    try { console.log(LOG_PREFIX, ...args); } catch {}
  }

  function schedule(delayMs) {
    try { clearTimeout(timer); } catch {}
    timer = setTimeout(() => { void runOnce(); }, Math.max(1000, Number(delayMs) || PERSONAL_INTERVAL_MS));
  }

  function getToken() {
    try { return String(localStorage.getItem('token') || '').trim(); } catch { return ''; }
  }

  function getStorage(keys) {
    return new Promise((resolve) => {
      try { chrome.storage.local.get(keys, (res) => resolve(res || {})); }
      catch { resolve({}); }
    });
  }

  async function getConfig() {
    const data = await getStorage(['ahSet', 'ahStop', 'ahRunning']);
    const set = data && typeof data.ahSet === 'object' && data.ahSet ? data.ahSet : {};
    const stopRaw = String(data?.ahStop || '');
    const stopSet = new Set(
      stopRaw
        .split(/[\s,;\n\r\t]+/)
        .map((v) => String(v || '').trim().toLowerCase())
        .filter(Boolean)
    );
    return {
      personalCollectorStopped: !!set.persons,
      botStopped: data?.ahRunning === false || set?.running === false,
      persToMaybe: !!set.perstomaybe,
      persToSpecial: !!set.perstospecial,
      stopSet
    };
  }

  async function apiJson(path, options = {}) {
    const token = getToken();
    if (!token) throw new Error('No token');

    const method = options.method || 'GET';
    const headers = {
      accept: 'application/json, text/plain, */*',
      authorization: `Bearer ${token}`,
      'x-requested-with': 'XMLHttpRequest',
      ...(options.headers || {})
    };
    const fetchOptions = {
      method,
      credentials: 'include',
      headers,
      body: options.body ? JSON.stringify(options.body) : undefined
    };
    if (options.body) headers['content-type'] = 'application/json;charset=UTF-8';

    const response = await fetch(`https://alpha.date${path}`, fetchOptions);
    const text = await response.text();
    let json = null;
    try { json = text ? JSON.parse(text) : null; } catch { json = null; }
    if (!response.ok) {
      throw new Error(`HTTP ${response.status} ${path}`);
    }
    return json;
  }

  async function fetchPersonalHistory() {
    const json = await apiJson('/api/personal-invites/personal-invites-list', { method: 'GET' });
    return Array.isArray(json?.historyList) ? json.historyList : [];
  }

  function isZeroOrEmpty(value) {
    return value === 0 || value === '0' || value === '' || value == null;
  }

  async function resolveChatUidForPair(womanExternalId, manExternalId) {
    const womanNum = Number(String(womanExternalId || '').trim());
    const manNum = Number(String(manExternalId || '').trim());
    if (!Number.isFinite(womanNum) || !Number.isFinite(manNum)) return { chatUid: null, row: null };

    const json = await apiJson('/api/chatList/chatListByUserID', {
      method: 'POST',
      body: {
        user_id: manNum,
        chat_uid: true,
        page: 1,
        freeze: true,
        limits: null,
        ONLINE_STATUS: null,
        SEARCH: '',
        CHAT_TYPE: '',
        external_id: womanNum
      }
    });

    const list = Array.isArray(json?.response) ? json.response : [];
    if (!list.length) return { chatUid: null, row: null };

    const row = list.find((item) => {
      const maleId = Number(item?.male_id ?? item?.maleId ?? item?.recipient_id ?? item?.recipientId);
      const maleExt = Number(item?.recipient_external_id ?? item?.recipientExternalId ?? item?.male_external_id ?? item?.maleExternalId);
      const femaleId = Number(item?.female_id ?? item?.femaleId ?? item?.sender_id ?? item?.senderId ?? item?.external_id);
      return (Number.isFinite(maleId) && maleId === manNum) ||
             (Number.isFinite(maleExt) && maleExt === manNum) ||
             (Number.isFinite(femaleId) && femaleId === womanNum);
    }) || list[0] || null;

    const chatUid = String(row?.chat_uid ?? row?.chatUid ?? '').trim();
    return { chatUid: chatUid || null, row };
  }

  function passesChatFilter(row) {
    if (!row || typeof row !== 'object') return false;
    if (!String(row?.chat_uid ?? '').trim()) return false;
    if (Number(row?.status ?? 0) !== 1) return false;
    if (!isZeroOrEmpty(row?.hide_chat)) return false;
    if (!isZeroOrEmpty(row?.male_block)) return false;
    if (!isZeroOrEmpty(row?.female_block)) return false;
    if (!isZeroOrEmpty(row?.male_hide)) return false;
    if (!isZeroOrEmpty(row?.female_hide)) return false;
    if (!(Number(row?.total_limit ?? 0) > 0)) return false;
    return true;
  }

  async function isEmptyChat(chatUid) {
    const json = await apiJson('/api/chatList/chatHistory', {
      method: 'POST',
      body: { chat_id: chatUid, page: 1 }
    });
    const response = Array.isArray(json?.response) ? json.response : [];
    return response.length === 0;
  }

  async function setMaybe(womanExternalId, chatUid) {
    return apiJson('/api/chat/setMaybe', {
      method: 'POST',
      body: {
        user_id: Number(womanExternalId),
        chat_uid: chatUid,
        chat_id: chatUid,
        maybe: 1
      }
    });
  }

  async function setSpecial(womanExternalId, chatUid) {
    return apiJson('/api/chat/setSpecial', {
      method: 'POST',
      body: {
        user_id: Number(womanExternalId),
        chat_uid: chatUid,
        chat_id: chatUid,
        special: 1
      }
    });
  }

  async function processHistoryItems(items, config) {
    const seenPairs = new Set();
    let maybeCount = 0;
    let specialCount = 0;

    for (const item of items) {
      if (!item || Number(item?.active) === 0) continue;

      const manExternalId = String(item?.man_external_id ?? item?.man_externalId ?? '').trim();
      const womanExternalId = String(item?.woman_external_id ?? item?.woman_externalId ?? '').trim();
      if (!manExternalId || !womanExternalId) continue;
      if (config.stopSet.has(manExternalId.toLowerCase())) continue;

      const pairKey = `${womanExternalId}::${manExternalId}`;
      if (seenPairs.has(pairKey)) continue;
      seenPairs.add(pairKey);

      let resolved;
      try {
        resolved = await resolveChatUidForPair(womanExternalId, manExternalId);
      } catch (error) {
        log('resolve failed', pairKey, error?.message || String(error));
        continue;
      }

      const chatUid = String(resolved?.chatUid || '').trim();
      const row = resolved?.row || null;
      if (!chatUid || !passesChatFilter(row)) continue;

      if (config.persToMaybe) {
        try {
          await setMaybe(womanExternalId, chatUid);
          maybeCount += 1;
        } catch (error) {
          log('setMaybe failed', chatUid, error?.message || String(error));
        }
      }

      if (config.persToSpecial) {
        try {
          const empty = await isEmptyChat(chatUid);
          if (empty) {
            await setSpecial(womanExternalId, chatUid);
            specialCount += 1;
          }
        } catch (error) {
          log('setSpecial failed', chatUid, error?.message || String(error));
        }
      }
    }

    return { maybeCount, specialCount, pairs: seenPairs.size };
  }

  async function runOnce() {
    if (running) return;
    running = true;
    try {
      const config = await getConfig();
      if (config.botStopped || config.personalCollectorStopped) {
        schedule(PERSONAL_INTERVAL_MS);
        return;
      }

      const history = await fetchPersonalHistory();
      const result = await processHistoryItems(history, config);
      log(`done history=${history.length} pairs=${result.pairs} maybe=${result.maybeCount} special=${result.specialCount}`);
      schedule(PERSONAL_INTERVAL_MS);
    } catch (error) {
      log('collector error', error?.message || String(error));
      schedule(PERSONAL_RETRY_MS);
    } finally {
      running = false;
    }
  }

  void runOnce();
})();
