'use strict';

var BKS = (function () {

  function parseKeywords(raw) {
    var seen = Object.create(null);
    var out = [];
    (raw || '').split('\n').forEach(function (line) {
      var k = line.trim();
      var key = k.toLowerCase();
      if (k && !seen[key]) { seen[key] = 1; out.push(k); }
    });
    return out;
  }

  function cleanDomain(raw) {
    var d = (raw || '').trim().toLowerCase();
    d = d.replace(/^https?:\/\//, '').replace(/^www\./, '');
    d = d.split('/')[0].split('?')[0].split('#')[0];
    return d;
  }

  function escapeHtml(s) {
    return String(s).replace(/[&<>"']/g, function (c) {
      return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c];
    });
  }

  function jobToRows(job) {
    return job.keywords.map(function (kw) {
      var e = (job.results && job.results[kw]) || {};
      return {
        keyword: kw,
        rank: (e.status === 'Found' ? e.rank : ''),
        url: e.url || '',
        status: e.status || 'Pending'
      };
    });
  }

  function toCsv(rows, locationLabel) {
    var esc = function (v) { return '"' + String(v == null ? '' : v).replace(/"/g, '""') + '"'; };
    var head = ['Keyword', 'Rank', 'Ranking URL', 'Location', 'Status'];
    var lines = [head.map(esc).join(',')];
    rows.forEach(function (r) {
      lines.push([r.keyword, r.rank, r.url, locationLabel || '', r.status].map(esc).join(','));
    });
    return lines.join('\r\n');
  }

  function triggerDownload(url, name) {
    var a = document.createElement('a');
    a.href = url; a.download = name;
    document.body.appendChild(a); a.click(); a.remove();
  }

  function flash(btn, text) {
    var orig = btn.dataset.orig || btn.textContent;
    btn.dataset.orig = orig;
    btn.textContent = text;
    setTimeout(function () { btn.textContent = btn.dataset.orig; }, 1200);
  }

  var SAVED_KEY = 'savedQueries';

  function loadQueries() {
    return chrome.storage.local.get(SAVED_KEY).then(function (d) { return d[SAVED_KEY] || []; });
  }
  function storeQueries(list) {
    var o = {}; o[SAVED_KEY] = list; return chrome.storage.local.set(o);
  }

  function saveQuery(entry) {
    return loadQueries().then(function (list) {
      list = list.filter(function (q) { return q.name.toLowerCase() !== entry.name.toLowerCase(); });
      list.unshift(entry);
      if (list.length > 50) list = list.slice(0, 50);
      return storeQueries(list).then(function () { return list; });
    });
  }
  function deleteQuery(id) {
    return loadQueries().then(function (list) {
      list = list.filter(function (q) { return q.id !== id; });
      return storeQueries(list).then(function () { return list; });
    });
  }

  var TIPS = [
    'Check <strong>10–15 keywords at a time</strong>. Smaller batches look human to Google, so you hit far fewer CAPTCHAs than a giant list.',
    'Turn on <strong>“Allow in Incognito”</strong> for this extension (chrome://extensions). Checks then run fully signed-out, so your own search history can’t skew the ranks — and CAPTCHAs drop sharply.',
    'Search your <strong>exact city, area or PIN code</strong> in the Location box, not just the country. Google ranks differently street-by-street — “country only” is far too vague.',
    'Running lots of keywords? <strong>Split them across a few smaller runs</strong> with short breaks. Google rate-limits bursts, not steady checking.',
    'If a CAPTCHA pops up, <strong>solve it once</strong> in the open tab and hit Resume — the extension keeps Google’s trust cookie so it won’t keep asking this run.',
    'Use <strong>Background mode</strong> to run checks silently while you keep working — it won’t take over your screen.',
    'Flip <strong>Device to Mobile</strong> to see the mobile SERP. Rankings often differ from desktop, and most searches now happen on phones.',
    '<strong>Save a keyword set</strong> with the Save button, then re-run it next week to track how your rankings move over time.',
    '<strong>Export to CSV</strong> and drop it into Google Sheets to chart your rankings and spot trends.',
    'Set <strong>Results language</strong> to match your audience (Hindi, Spanish, …). Google returns different pages per language.',
    'Open the <strong>Full-tab dashboard</strong> (top-right button) for a bigger table with ranking URLs and one-click CSV export.',
    'For <strong>country-exact</strong> numbers, run the check from that country’s IP (a VPN works) — Google still nudges results by where the request comes from.',
    'Tick <strong>“Use this location for all my Google searches”</strong> under the Location box to browse Google as if you were in that city — every search you type is localized there until you untick it.',
    'On <strong>Microsoft Edge</strong> (or any fresh browser profile): open google.com, run one normal search and sign in if you can, THEN start a check. Google challenges cookie-less strangers with CAPTCHAs — a warmed-up, signed-in session is trusted.',
    'Getting CAPTCHAs on every run? Choose <strong>“This tab”</strong> when starting — it reuses your everyday session, which Google trusts far more than a fresh signed-out one.'
  ];
  function tipAt(i) {
    var n = TIPS.length;
    return TIPS[(((i || 0) % n) + n) % n];
  }

  var HISTORY_KEY = 'rankHistory';

  function loadHistory() {
    return chrome.storage.local.get(HISTORY_KEY).then(function (d) { return d[HISTORY_KEY] || []; });
  }
  function storeHistory(list) {
    var o = {}; o[HISTORY_KEY] = list; return chrome.storage.local.set(o);
  }

  function saveSnapshot(job) {
    var snap = {
      id: 's' + Date.now().toString(36),
      savedAt: new Date().toISOString(),
      target: job.target || '',
      locationLabel: job.locationLabel || '',
      gl: job.gl || '',
      results: {}
    };
    (job.keywords || []).forEach(function (kw) {
      var e = (job.results && job.results[kw]) || {};
      snap.results[kw] = {
        rank: (e.status === 'Found' ? e.rank : ''),
        url: e.url || '',
        status: e.status || 'Pending'
      };
    });
    return loadHistory().then(function (list) {
      list.unshift(snap);
      if (list.length > 300) list = list.slice(0, 300);
      return storeHistory(list).then(function () { return snap; });
    });
  }
  function deleteSnapshot(id) {
    return loadHistory().then(function (list) {
      return storeHistory(list.filter(function (s) { return s.id !== id; }));
    });
  }

  function previousSnapshot(list, snap) {
    var i = list.findIndex(function (s) { return s.id === snap.id; });
    if (i < 0) return null;
    for (var j = i + 1; j < list.length; j++) {
      if (list[j].target === snap.target) return list[j];
    }
    return null;
  }

  function rankDelta(curr, prev) {
    var c = (curr === '' || curr == null) ? null : Number(curr);
    var p = (prev === '' || prev == null) ? null : Number(prev);
    if (c == null && p == null) return { dir: 'none', by: 0 };
    if (c != null && p == null) return { dir: 'new', by: 0 };
    if (c == null && p != null) return { dir: 'lost', by: 0 };
    if (c < p) return { dir: 'up', by: p - c };
    if (c > p) return { dir: 'down', by: c - p };
    return { dir: 'same', by: 0 };
  }

  function formatWhen(iso) {
    var d = new Date(iso);
    if (isNaN(d)) return iso;
    return d.toLocaleString(undefined, { day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit' });
  }

  function openRunTabChooser(opts) {
    opts = opts || {};
    var allowCurrent = opts.allowCurrent !== false;

    function icoHtml(tab, fallback) {
      var u = tab && tab.favIconUrl || '';
      if (/^(https?:|data:)/.test(u)) return '<img class="pop-chip-fav" src="' + escapeHtml(u) + '" alt="" width="16" height="16" data-fallback="1">';
      return '<svg class="ic" aria-hidden="true"><use href="#' + (fallback || 'i-globe') + '"></use></svg>';
    }

    function wireFavFallback(root) {
      root.querySelectorAll('img[data-fallback]').forEach(function (img) {
        img.addEventListener('error', function () {
          img.outerHTML = '<svg class="ic" aria-hidden="true"><use href="#i-globe"></use></svg>';
        }, { once: true });
      });
    }

    return Promise.all([
      chrome.tabs.query({}),
      allowCurrent ? chrome.tabs.query({ active: true, currentWindow: true }) : Promise.resolve([]),
      chrome.storage.local.get(['ui.runTab', 'ui.incognito']),
      new Promise(function (res) {
        try { chrome.extension.isAllowedIncognitoAccess(function (ok) { res(!!ok); }); }
        catch (e) { res(false); }
      })
    ]).then(function (r) {
      var self = chrome.runtime.getURL('');
      var act = r[1] && r[1][0];
      var currentTab = (act && act.id != null && (act.url || '').indexOf(self) !== 0
        && (act.url || '').indexOf('devtools:') !== 0) ? act : null;
      var remembered = r[2]['ui.runTab'];
      var incogAllowed = r[3];
      var incogOn = incogAllowed && (r[2]['ui.incognito'] !== false);
      var tabsById = Object.create(null);
      var rows = r[0].filter(function (t) {
        return t.id != null && (t.url || '').indexOf(self) !== 0 && (t.url || '').indexOf('devtools:') !== 0
          && !(currentTab && t.id === currentTab.id);
      });
      rows.forEach(function (t) { tabsById[t.id] = t; });

      var html = '';
      if (currentTab) {
        html += '<div class="runpick-opt" role="button" tabindex="0" data-tab="current">'
          + '<span class="runpick-opt-ico">' + icoHtml(currentTab) + '</span>'
          + '<span class="runpick-opt-body"><span class="runpick-opt-title">This tab</span>'
          + '<span class="runpick-opt-desc">Right here, where you are — you’ll see every search happen.</span></span>'
          + (currentTab.incognito ? '<span class="pop-chip-count">Incognito</span>' : '')
          + '</div>';
      }

      html += '<div class="runpick-opt" role="button" tabindex="0" data-tab="new">'
        + '<span class="runpick-opt-ico"><svg class="ic" aria-hidden="true"><use href="#i-plus"></use></svg></span>'
        + '<span class="runpick-opt-body"><span class="runpick-opt-title">A new tab</span>'
        + '<span class="runpick-opt-desc">Opens fresh and signed out — the cleanest rankings.</span>'
        + '<span class="runpick-incog">'
        +   '<span class="runpick-incog-label">Open it in Incognito</span>'
        +   '<span class="runpick-incog-note"></span>'
        +   '<button type="button" class="pop-inctog runpick-incog-switch" role="switch" aria-checked="false">'
        +     '<span class="pop-inctog-track" aria-hidden="true"></span>'
        +     '<span class="pop-inctog-knob" aria-hidden="true"><svg class="ic"><use href="#i-incognito"></use></svg></span>'
        +   '</button>'
        + '</span>'
        + '</span></div>';
      var tabsHtml = rows.map(function (t) {
        var name = t.title || t.url || 'Tab';
        return '<div class="pop-srow" role="button" tabindex="0" data-tab="' + t.id + '" title="' + escapeHtml(name) + '">'
          + '<span class="pop-runtab-fav">' + icoHtml(t) + '</span>'
          + '<span class="pop-srow-name">' + escapeHtml(name) + '</span>'
          + (t.incognito ? '<span class="pop-chip-count">Incognito</span>' : '')
          + '</div>';
      }).join('');

      var ovl = document.createElement('div');
      ovl.className = 'runpick-ovl';
      ovl.innerHTML = '<div class="runpick" role="dialog" aria-modal="true" aria-labelledby="runpick-title">'
        + '<h3 class="runpick-title" id="runpick-title">Where should the check run?</h3>'
        + html
        + (rows.length
          ? '<button type="button" class="runpick-more" aria-expanded="false">Use a tab that’s already open'
            + ' <svg class="ic" aria-hidden="true"><use href="#i-chevron-down"></use></svg></button>'
            + '<div class="runpick-tabs" hidden>' + tabsHtml + '</div>'
          : '')
        + '<div class="runpick-foot"><button type="button" class="runpick-cancel">Cancel</button></div>'
        + '</div>';
      document.body.appendChild(ovl);
      wireFavFallback(ovl);

      var incogRow = ovl.querySelector('.runpick-incog');
      var incogSwitch = ovl.querySelector('.runpick-incog-switch');
      var incogNote = ovl.querySelector('.runpick-incog-note');
      function renderIncog() {
        incogSwitch.classList.toggle('is-on', incogOn && incogAllowed);
        incogSwitch.classList.toggle('is-disabled', !incogAllowed);
        incogSwitch.setAttribute('aria-checked', String(!!(incogOn && incogAllowed)));
        incogNote.textContent = incogAllowed ? '' : 'needs “Allow in Incognito”';
        incogRow.title = incogAllowed
          ? 'A private window — your own logins and history can’t skew the ranks.'
          : 'Click to open this extension’s settings and turn on “Allow in Incognito”.';
      }
      renderIncog();
      incogRow.addEventListener('click', function (e) {
        e.stopPropagation();
        if (!incogAllowed) { chrome.tabs.create({ url: 'chrome://extensions/?id=' + chrome.runtime.id }); return; }
        incogOn = !incogOn;
        chrome.storage.local.set({ 'ui.incognito': incogOn });
        renderIncog();
      });

      var moreBtn = ovl.querySelector('.runpick-more');
      var tabsBox = ovl.querySelector('.runpick-tabs');
      function toggleTabs(open) {
        if (!moreBtn) return;
        var show = (open != null) ? open : tabsBox.hidden;
        tabsBox.hidden = !show;
        moreBtn.setAttribute('aria-expanded', String(show));
      }
      if (moreBtn) moreBtn.addEventListener('click', function () { toggleTabs(); });

      var selKey = currentTab ? 'current' : 'new';
      if (remembered === 'new' || (remembered === 'current' && currentTab)) selKey = remembered;
      else if (typeof remembered === 'number') {
        if (currentTab && remembered === currentTab.id) selKey = 'current';
        else if (tabsById[remembered]) { selKey = String(remembered); toggleTabs(true); }
      }
      var selRow = ovl.querySelector('[data-tab="' + selKey + '"]');
      if (selRow) selRow.classList.add('is-sel');

      return new Promise(function (resolve) {
        function close(result) {
          document.removeEventListener('keydown', onKey, true);
          ovl.remove();
          resolve(result);
        }
        function pick(v) {
          var incog = incogAllowed && incogOn;
          if (v === 'current') {
            chrome.storage.local.set({ 'ui.runTab': 'current' });

            close({ tabId: currentTab.id, isCurrent: true, incognito: incog });
          } else if (v === 'new') {
            chrome.storage.local.set({ 'ui.runTab': 'new' });
            close({ tabId: null, isCurrent: false, incognito: incog });
          } else {
            chrome.storage.local.set({ 'ui.runTab': Number(v) });
            close({ tabId: Number(v), isCurrent: false, incognito: incog });
          }
        }
        function onKey(e) {
          if (e.key === 'Escape') { e.stopPropagation(); close(null); }
          else if ((e.key === 'Enter' || e.key === ' ') && e.target.dataset && e.target.dataset.tab) {
            e.preventDefault(); pick(e.target.dataset.tab);
          }
        }
        document.addEventListener('keydown', onKey, true);
        ovl.addEventListener('click', function (e) {
          if (e.target === ovl) { close(null); return; }
          if (e.target.closest('.runpick-cancel')) { close(null); return; }
          var row = e.target.closest('[data-tab]');
          if (row) pick(row.dataset.tab);
        });
        var first = ovl.querySelector('.is-sel') || ovl.querySelector('.runpick-opt');
        if (first) first.focus();
      });
    });
  }

  function mountKeywordSummary(els) {
    var ta = els.textarea, box = els.summary, count = els.count, detail = els.detail, edit = els.edit;
    function collapse() {
      var kws = parseKeywords(ta.value);
      if (!kws.length || document.activeElement === ta) return;
      count.textContent = kws.length + (kws.length === 1 ? ' keyword' : ' keywords');
      var preview = kws.slice(0, 3).join(' · ');
      detail.textContent = preview + (kws.length > 3 ? ' …' : '');
      box.hidden = false;
      ta.hidden = true;
    }
    function expand(focus) {
      box.hidden = true;
      ta.hidden = false;
      if (focus !== false) ta.focus();
    }
    ta.addEventListener('blur', function () { setTimeout(collapse, 150); });
    edit.addEventListener('click', function () { expand(true); });
    return { collapse: collapse, expand: expand };
  }

  function rankCellHtml(entry) {
    if (!entry) return '<span class="rank-badge rank-pending">…</span>';
    if (entry.status === 'Found' && entry.rank !== '' && entry.rank != null) {
      return '<span class="rank-badge rank-hit">#' + entry.rank + '</span>';
    }
    return '<span class="rank-badge rank-miss" title="' + escapeHtml(entry.status || 'Not found') + '">—</span>';
  }

  return {
    parseKeywords: parseKeywords,
    cleanDomain: cleanDomain,
    escapeHtml: escapeHtml,
    jobToRows: jobToRows,
    toCsv: toCsv,
    triggerDownload: triggerDownload,
    flash: flash,
    rankCellHtml: rankCellHtml,
    openRunTabChooser: openRunTabChooser,
    mountKeywordSummary: mountKeywordSummary,
    loadQueries: loadQueries,
    saveQuery: saveQuery,
    deleteQuery: deleteQuery,
    TIPS: TIPS,
    tipAt: tipAt,
    loadHistory: loadHistory,
    saveSnapshot: saveSnapshot,
    deleteSnapshot: deleteSnapshot,
    previousSnapshot: previousSnapshot,
    rankDelta: rankDelta,
    formatWhen: formatWhen
  };
})();
