(function () {
  'use strict';

  // Убираем старые инжект-стили (если остались от прошлых версий)
  ['ted-kpi-rays-style', 'ted-kpi-rays-style-v12'].forEach(function(id){
    var n = document.getElementById(id);
    if (n && n.parentNode) n.parentNode.removeChild(n);
  });

  if (window.__TED_KPI_WIDGET_SUN_V1__) return;
  window.__TED_KPI_WIDGET_SUN_V1__ = true;

  var CFG = {
    containerSelector: '.ted-kpi-rings',
    trainingPathRe: /\/teach\/control\/stream\/view\/id\/\d+/,
    lessonPathRe: /\/teach\/control\/lesson\/view\/id\/\d+/,

    bannedClosestSelector: [
      '.gc-account-leftbar','header','footer',
      '.breadcrumb','.breadcrumbs','.gc-breadcrumbs',
      '.nav','.nav-tabs','.tabs',
      '.dropdown','.dropdown-menu','.btn-group','[role="menu"]',
      '.modal','.popover'
    ].join(','),

    contentRootSelector: ['#gcAccountContent','#content','.gc-main-content','.page-content','.container','body'].join(','),

    hardDoneClasses: ['user-state-accomplished','user-state-answered','user-state-finished','user-state-completed'],
    reachedClass: 'user-state-reached',
    missionClass: 'user-state-has_mission',
    checkIconSelectors: '.fa-check, .glyphicon-ok, .icon-check',

    maxDepthLessons: 6,
    cacheTtlMs: 20 * 60 * 1000,
    aggCacheKey: 'ted_kpi_agg_cache_sun_v1',
    snapCacheKey: 'ted_kpi_snap_cache_sun_v1',
    debug: false
  };

  function log(){ if (CFG.debug) console.log.apply(console, arguments); }
  function clamp(n,a,b){ return Math.max(a, Math.min(b, n)); }

  function inBannedUI(node){ return node && node.closest && node.closest(CFG.bannedClosestSelector); }
  function getContentRoot(doc){
    doc = doc || document;
    return doc.querySelector(CFG.contentRootSelector) || doc.body;
  }

  function resolveHref(href, baseUrl){
    try { return new URL(href, baseUrl).toString(); } catch(e){ return null; }
  }

  function canonicalTrainingUrl(url, base) {
    try {
      var u = new URL(url, base || location.href);
      u.hash = ''; u.search = '';
      var m = u.pathname.match(CFG.trainingPathRe);
      if (m) u.pathname = m[0];
      return u.toString();
    } catch (e) {
      var s = String(url || '').split('#')[0].split('?')[0];
      var m2 = s.match(CFG.trainingPathRe);
      return m2 ? m2[0] : s;
    }
  }

  function canonicalLessonUrl(url, base) {
    try {
      var u = new URL(url, base || location.href);
      u.hash = ''; u.search = '';
      var m = u.pathname.match(CFG.lessonPathRe);
      if (m) u.pathname = m[0];
      return u.toString();
    } catch (e) {
      var s = String(url || '').split('#')[0].split('?')[0];
      var m2 = s.match(CFG.lessonPathRe);
      return m2 ? m2[0] : s;
    }
  }

  function idFromUrl(url){
    var m = String(url).match(/\/id\/(\d+)/);
    return m ? m[1] : String(url);
  }

  function pluralRu(n, one, few, many) {
    n = Math.abs(n) % 100;
    var n1 = n % 10;
    if (n > 10 && n < 20) return many;
    if (n1 > 1 && n1 < 5) return few;
    if (n1 === 1) return one;
    return many;
  }
  function wTrain(n){ return pluralRu(n,'тренинг','тренинга','тренингов'); }
  function wLevel(n){ return pluralRu(n,'уровень','уровня','уровней'); }
  function wLesson(n){ return pluralRu(n,'урок','урока','уроков'); }

  function loadCache(key){
    try { return JSON.parse(localStorage.getItem(key) || '{}'); } catch(e){ return {}; }
  }
  function saveCache(key, obj){
    try { localStorage.setItem(key, JSON.stringify(obj)); } catch(e){}
  }
  function getCached(cacheObj, id){
    var v = cacheObj[id];
    if (!v) return null;
    if ((Date.now() - v.ts) > CFG.cacheTtlMs) return null;
    return v.payload || null;
  }
  function setCached(cacheObj, key, id, payload){
    cacheObj[id] = { payload: payload, ts: Date.now() };
    saveCache(key, cacheObj);
  }

  // DONE-detect
  function hasAnyClass(el, arr){
    if (!el || !el.classList) return false;
    for (var i=0;i<arr.length;i++) if (el.classList.contains(arr[i])) return true;
    return false;
  }
  function getStateCarrier(el){
    if (!el) return el;
    var carrier = el.closest ? (el.closest('li, tr, .lesson') || el) : el;
    var cn = String(carrier.className || '');
    if (cn.indexOf('user-state-') !== -1) return carrier;
    var inner = carrier.querySelector ? carrier.querySelector('[class*="user-state-"]') : null;
    return inner || carrier;
  }
  function isLessonDone(carrier){
    if (!carrier) return false;
    if (hasAnyClass(carrier, CFG.hardDoneClasses)) return true;

    if (carrier.classList && carrier.classList.contains(CFG.reachedClass)) {
      if (!carrier.classList.contains(CFG.missionClass)) return true;
    }
    if (carrier.classList && carrier.classList.contains(CFG.missionClass)) return false;

    if (carrier.querySelector && carrier.querySelector(CFG.checkIconSelectors)) return true;
    return false;
  }

  function extractLessonLinks(doc, baseUrl){
    var root = getContentRoot(doc);
    var anchors = Array.from(root.querySelectorAll('a[href]'));
    var out = [];

    anchors.forEach(function(a){
      if (inBannedUI(a)) return;
      var abs = resolveHref(a.getAttribute('href'), baseUrl);
      if (!abs) return;
      var canon = canonicalLessonUrl(abs, baseUrl);
      if (!CFG.lessonPathRe.test(canon)) return;
      out.push({ a:a, url:canon });
    });

    var seen = new Set();
    return out.filter(function(x){
      if (seen.has(x.url)) return false;
      seen.add(x.url);
      return true;
    });
  }

  function countLessons(doc, baseUrl){
    var links = extractLessonLinks(doc, baseUrl);
    if (!links.length) return { done:0, total:0 };

    var total = links.length;
    var done = 0;
    links.forEach(function(x){
      var host = getStateCarrier(x.a);
      if (isLessonDone(host)) done++;
    });

    done = clamp(done, 0, total);
    return { done:done, total:total };
  }

  function extractChildTrainings(doc, baseUrl){
    var root = getContentRoot(doc);
    var anchors = Array.from(root.querySelectorAll('a[href]'));

    var baseCanon = canonicalTrainingUrl(baseUrl, baseUrl);
    var baseId = idFromUrl(baseCanon);

    var byId = new Map();
    anchors.forEach(function(a){
      if (inBannedUI(a)) return;
      var abs = resolveHref(a.getAttribute('href'), baseUrl);
      if (!abs) return;

      var canon = canonicalTrainingUrl(abs, baseUrl);
      if (!CFG.trainingPathRe.test(canon)) return;

      var id = idFromUrl(canon);
      if (id === baseId) return;

      // лёгкая фильтрация закрытых/disabled
      var txt = (a.textContent || '').toLowerCase();
      if (txt.includes('доступ закрыт')) return;
      if (a.getAttribute('aria-disabled') === 'true') return;
      if ((a.className || '').toLowerCase().includes('disabled')) return;

      if (!byId.has(id)) byId.set(id, canon);
    });

    return Array.from(byId.values());
  }

  async function fetchDoc(url){
    var resp = await fetch(url, { credentials:'include' });
    if (!resp.ok) throw new Error('HTTP ' + resp.status);
    var html = await resp.text();
    return new DOMParser().parseFromString(html, 'text/html');
  }

  // Snapshots + aggregate
  var memSnap = new Map();
  async function getSnapshot(url, snapCache){
    var canon = canonicalTrainingUrl(url, url);
    var id = idFromUrl(canon);

    if (memSnap.has(id)) return memSnap.get(id);

    var cached = getCached(snapCache, id);
    if (cached) { memSnap.set(id, cached); return cached; }

    var doc = await fetchDoc(canon);
    var ls = countLessons(doc, canon);
    var kids = extractChildTrainings(doc, canon);

    var payload = { url:canon, lessons:ls, children:kids };
    memSnap.set(id, payload);
    setCached(snapCache, CFG.snapCacheKey, id, payload);
    return payload;
  }

  var memAgg = new Map();
  var inFlightAgg = new Map();

  async function aggregateLessons(url, depth, visited, aggCache, snapCache){
    var canon = canonicalTrainingUrl(url, url);
    var id = idFromUrl(canon);

    if (visited.has(id)) return { done:0, total:0 };
    visited.add(id);

    if (memAgg.has(id)) return memAgg.get(id);

    var cached = getCached(aggCache, id);
    if (cached) { memAgg.set(id, cached); return cached; }

    if (inFlightAgg.has(id)) return await inFlightAgg.get(id);

    var p = (async function(){
      try {
        var snap = await getSnapshot(canon, snapCache);

        if (snap.lessons.total > 0) {
          var leaf = { done:snap.lessons.done, total:snap.lessons.total };
          memAgg.set(id, leaf);
          setCached(aggCache, CFG.aggCacheKey, id, leaf);
          return leaf;
        }

        if (depth >= CFG.maxDepthLessons || !snap.children.length) {
          var empty = { done:0, total:0 };
          memAgg.set(id, empty);
          setCached(aggCache, CFG.aggCacheKey, id, empty);
          return empty;
        }

        var sum = { done:0, total:0 };
        for (var i=0;i<snap.children.length;i++){
          var child = await aggregateLessons(snap.children[i], depth+1, visited, aggCache, snapCache);
          sum.done += child.done;
          sum.total += child.total;
        }

        memAgg.set(id, sum);
        setCached(aggCache, CFG.aggCacheKey, id, sum);
        return sum;
      } finally {
        inFlightAgg.delete(id);
      }
    })();

    inFlightAgg.set(id, p);
    return await p;
  }

  function guessIndexUrl(container){
    var explicit = container.getAttribute('data-index-url');
    if (explicit) { try { return new URL(explicit, location.origin).toString(); } catch(e){} }

    var p = location.pathname;
    var m = p.match(/^(.*\/teach\/control\/stream)(?:\/view\/id\/\d+)?/);
    if (m && m[1]) return location.origin + m[1];
    return location.origin + '/teach/control/stream';
  }

  async function getTrainingsFromIndex(indexUrl){
    var doc = await fetchDoc(indexUrl);
    var root = getContentRoot(doc);
    var anchors = Array.from(root.querySelectorAll('a[href]'));

    var byId = new Map();
    anchors.forEach(function(a){
      if (inBannedUI(a)) return;
      var abs = resolveHref(a.getAttribute('href'), indexUrl);
      if (!abs) return;

      var canon = canonicalTrainingUrl(abs, indexUrl);
      if (!CFG.trainingPathRe.test(canon)) return;

      var id = idFromUrl(canon);
      if (!byId.has(id)) byId.set(id, canon);
    });

    return Array.from(byId.values());
  }

  function iconSvg(kind){
    if (kind === 'train') return (
      '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true">' +
      '<path d="M7 4h10a2 2 0 0 1 2 2v14H5V6a2 2 0 0 1 2-2Z" stroke="white" stroke-width="2" opacity=".95"/>' +
      '<path d="M8 8h8M8 12h8M8 16h6" stroke="white" stroke-width="2" stroke-linecap="round" opacity=".95"/>' +
      '</svg>'
    );
    if (kind === 'level') return (
      '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true">' +
      '<path d="M7 10h10v4a5 5 0 0 1-10 0v-4Z" stroke="white" stroke-width="2" opacity=".95"/>' +
      '<path d="M9 6h6" stroke="white" stroke-width="2" stroke-linecap="round" opacity=".95"/>' +
      '</svg>'
    );
    return (
      '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" aria-hidden="true">' +
      '<path d="M10 8l8 4-8 4V8Z" fill="white" opacity=".95"/>' +
      '<path d="M5 7a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V7Z" stroke="white" opacity=".65"/>' +
      '</svg>'
    );
  }

  function render(container){
    container.innerHTML =
      '<div class="tedKPI">' +
      '  <div class="tedKPI__orb">' +
      '    <div class="tedKPI__orbGlow"></div>' +
      '    <div class="tedKPI__orbGlass"></div>' +
      '    <div class="tedKPI__orbRing"></div>' +
      '    <div class="tedKPI__orbContent">' +
      '      <div>' +
      '        <div class="tedKPI__num" data-kpi="lessDone">0</div>' +
      '        <div class="tedKPI__label">уроков</div>' +
      '        <div class="tedKPI__sub" data-kpi="lessSub">из 0 уроков</div>' +
      '      </div>' +
      '    </div>' +
      '  </div>' +

      '  <div class="tedKPI__rays">' +
      '    <div class="tedRay tedRay--train">' +
      '      <div class="tedRay__content">' +
      '        <div class="tedRay__left"><div class="tedRay__icon">'+iconSvg('train')+'</div><div class="tedRay__title">тренинги</div></div>' +
      '        <div class="tedRay__right"><span class="tedRay__value" data-kpi="trainTotal">—</span><span class="tedRay__unit" data-kpi="trainUnit">тренингов</span></div>' +
      '      </div>' +
      '    </div>' +

      '    <div class="tedRay tedRay--level">' +
      '      <div class="tedRay__content">' +
      '        <div class="tedRay__left"><div class="tedRay__icon">'+iconSvg('level')+'</div><div class="tedRay__title">уровни</div></div>' +
      '        <div class="tedRay__right"><span class="tedRay__value" data-kpi="levelTotal">—</span><span class="tedRay__unit" data-kpi="levelUnit">уровней</span></div>' +
      '      </div>' +
      '    </div>' +

      '    <div class="tedRay tedRay--lesson">' +
      '      <div class="tedRay__content">' +
      '        <div class="tedRay__left"><div class="tedRay__icon">'+iconSvg('lesson')+'</div><div class="tedRay__title">пройдено уроков</div></div>' +
      '        <div class="tedRay__right"><span class="tedRay__value" data-kpi="lessTotalDone">—</span><span class="tedRay__unit" data-kpi="lessUnit">уроков</span></div>' +
      '      </div>' +
      '    </div>' +
      '  </div>' +

      '  <div class="tedKPI__meta">' +
      '    <div class="tedKPI__metaTitle">Сводка обучения</div>' +
      '    <div class="tedKPI__metaLine" data-kpi="meta1">Сбор данных…</div>' +
      '    <div class="tedKPI__metaLine" data-kpi="meta2"></div>' +
      '  </div>' +
      '</div>';
  }

  function update(container, s){
    container.querySelector('[data-kpi="lessDone"]').textContent = String(s.lessonsDone||0);
    container.querySelector('[data-kpi="lessSub"]').textContent =
      'из ' + (s.lessonsTotal||0) + ' ' + wLesson(s.lessonsTotal||0);

    container.querySelector('[data-kpi="trainTotal"]').textContent = String(s.trainingsTotal||0);
    container.querySelector('[data-kpi="levelTotal"]').textContent = String(s.levelsTotal||0);
    container.querySelector('[data-kpi="lessTotalDone"]').textContent = String(s.lessonsDone||0);

    container.querySelector('[data-kpi="trainUnit"]').textContent = wTrain(s.trainingsTotal||0);
    container.querySelector('[data-kpi="levelUnit"]').textContent = wLevel(s.levelsTotal||0);
    container.querySelector('[data-kpi="lessUnit"]').textContent  = wLesson(s.lessonsDone||0);

    container.querySelector('[data-kpi="meta1"]').textContent =
      'Доступно: ' + (s.trainingsTotal||0) + ' ' + wTrain(s.trainingsTotal||0) +
      ', ' + (s.levelsTotal||0) + ' ' + wLevel(s.levelsTotal||0);

    container.querySelector('[data-kpi="meta2"]').textContent =
      'Пройдено: ' + (s.lessonsDone||0) + ' ' + wLesson(s.lessonsDone||0) +
      ' (из ' + (s.lessonsTotal||0) + ')';
  }

  async function compute(container){
    var indexUrl = guessIndexUrl(container);
    var aggCache = loadCache(CFG.aggCacheKey);
    var snapCache = loadCache(CFG.snapCacheKey);

    var trainings = [];
    try {
      trainings = await getTrainingsFromIndex(indexUrl);
    } catch (e) {
      if (CFG.trainingPathRe.test(location.pathname)) {
        trainings = [canonicalTrainingUrl(location.href, location.href)];
      } else {
        throw e;
      }
    }

    var s = {
      trainingsTotal: trainings.length,
      trainingsDone: 0,
      levelsTotal: 0,
      levelsDone: 0,
      lessonsTotal: 0,
      lessonsDone: 0
    };

    for (var i=0;i<trainings.length;i++){
      var tUrl = trainings[i];

      var agg = await aggregateLessons(tUrl, 0, new Set(), aggCache, snapCache);
      s.lessonsTotal += agg.total;
      s.lessonsDone  += agg.done;

      if (agg.total > 0 && agg.done === agg.total) s.trainingsDone++;

      var snap = await getSnapshot(tUrl, snapCache);
      var levels = snap.children || [];
      s.levelsTotal += levels.length;

      for (var j=0;j<levels.length;j++){
        var lvAgg = await aggregateLessons(levels[j], 1, new Set(), aggCache, snapCache);
        if (lvAgg.total > 0 && lvAgg.done === lvAgg.total) s.levelsDone++;
      }

      update(container, s); // живое обновление
    }

    update(container, s);
  }

  function initAll(){
    var nodes = document.querySelectorAll(CFG.containerSelector);
    Array.from(nodes).forEach(function(container){
      render(container);
      update(container, { trainingsTotal:0, levelsTotal:0, lessonsDone:0, lessonsTotal:0 });
      compute(container).catch(function(e){
        log('[TED KPI] compute failed', e);
        var meta1 = container.querySelector('[data-kpi="meta1"]');
        if (meta1) meta1.textContent = 'Не удалось посчитать: проверь data-index-url и доступ';
      });
    });
  }

  window.addEventListener('load', function(){
    setTimeout(function(){ try { initAll(); } catch(e){} }, 350);
  });

  window.TEDKpiSun = {
    clearCache: function(){
      try { localStorage.removeItem(CFG.aggCacheKey); } catch(e){}
      try { localStorage.removeItem(CFG.snapCacheKey); } catch(e){}
      console.log('[TED KPI] cache cleared');
    }
  };
})();
