/* Sponzza: SponsorSignal matching engine (classic content-script global). */

var SponzzaMatcher = (function () {
  var STRIP_WORDS = new Set([
    'ltd', 'limited', 'llp', 'llc', 'plc', 'inc', 'incorporated', 'corp',
    'corporation', 'co', 'company', 'group', 'holdings', 'holding', 'uk',
    'gb', 'gbr', 'the', 'and', 'intl', 'international', 'services', 'service',
    'solutions', 'global', 'europe', 'emea', 'unlimited',
  ]);

  function normalize(name) {
    if (!name) return '';
    var s = String(name)
      .toLowerCase()
      .normalize('NFKD').replace(/[\u0300-\u036f]/g, '')
      .replace(/&/g, ' and ')
      .replace(/[^a-z0-9\s]/g, ' ')
      .replace(/\s+/g, ' ')
      .trim();
    var kept = s.split(' ').filter(function (w) { return w && !STRIP_WORDS.has(w); });
    return kept.length ? kept.join(' ') : s.replace(/\s+/g, ' ').trim();
  }

  function Matcher() {
    this.byKey = new Map();
    this.signals = new Map();
    this.aliases = new Map();
    this.buckets = null;
    this.fuseCache = new Map();
    this.resultCache = new Map();
    this.ready = false;
    this.meta = {};
  }

  Matcher.prototype.normalize = normalize;

  Matcher.prototype.load = function (dataUrl, aliasUrl) {
    var self = this;
    return fetch(dataUrl)
      .then(function (r) { return r.json(); })
      .then(function (data) {
        self.byKey = new Map(Object.entries(data.byKey || {}));
        self.signals = new Map(Object.entries(data.signals || {}));
        self.meta = data.meta || {};
        if (!aliasUrl) return;
        return fetch(aliasUrl)
          .then(function (r) { return r.json(); })
          .then(function (aliasData) {
            Object.keys(aliasData).forEach(function (k) {
              self.aliases.set(normalize(k), aliasData[k]);
            });
          })
          .catch(function () { /* aliases optional */ });
      })
      .then(function () { self.ready = true; return self.meta; });
  };

  Matcher.prototype._ensureBuckets = function () {
    if (this.buckets) return;
    this.buckets = new Map();
    var keys = this.byKey.keys();
    var next = keys.next();
    while (!next.done) {
      var key = next.value;
      var t = key.split(' ', 1)[0];
      var arr = this.buckets.get(t);
      if (!arr) { arr = []; this.buckets.set(t, arr); }
      arr.push(key);
      next = keys.next();
    }
  };

  Matcher.prototype._fuseFor = function (token) {
    var fuse = this.fuseCache.get(token);
    if (fuse) return fuse;
    var bucket = this.buckets.get(token) || [];
    fuse = new SponzzaFuse(bucket, {
      threshold: 0.2, distance: 60, minMatchCharLength: 3,
      ignoreLocation: true, includeScore: true,
    });
    this.fuseCache.set(token, fuse);
    return fuse;
  };

  Matcher.prototype._sig = function (key) {
    var raw = this.signals.get(key) || 'A11';
    return { rating: raw[0]||'A', sw: raw[1]==='1', routes: parseInt(raw[2],10)||1 };
  };

  Matcher.prototype.match = function (companyName) {
    if (!this.ready) return { status: 'red', matchedName: null, score: 0, method: 'not-ready' };
    var key = normalize(companyName);
    if (!key) return { status: 'red', matchedName: null, score: 0, method: 'empty' };
    if (this.resultCache.has(key)) return this.resultCache.get(key);

    var result;
    if (this.byKey.has(key)) {
      result = { status: 'green', matchedName: this.byKey.get(key), score: 1, method: 'exact', signal: this._sig(key) };
    } else if (this.aliases.has(key)) {
      result = { status: 'green', matchedName: this.aliases.get(key), score: 1, method: 'alias', signal: this._sig(key) };
    } else {
      this._ensureBuckets();
      var token = key.split(' ', 1)[0];
      var hits = this._fuseFor(token).search(key, { limit: 1 });
      if (hits.length && hits[0].score <= 0.2) {
        result = {
          status: 'green',
          matchedName: this.byKey.get(hits[0].item) || hits[0].item,
          score: 1 - hits[0].score,
          method: 'fuzzy',
          signal: this._sig(hits[0].item),
        };
      } else {
        result = { status: 'red', matchedName: null, score: 0, method: 'no-match' };
      }
    }
    this.resultCache.set(key, result);
    return result;
  };

  return Matcher;
})();
