(function () {
  'use strict';
  if (window.__TED_SUN_LITE_V1__) return;
  window.__TED_SUN_LITE_V1__ = true;

  var CFG = {
    rootSel: '.ted-sun-lite',
    trainingRe: /\/teach\/control\/stream\/view\/id\/\d+/,
    lessonRe: /\/teach\/control\/lesson\/view\/id\/\d+/,
    bannedClosest: [
      '.gc-account-leftbar','header','footer',
      '.breadcrumb','.breadcrumbs','.gc-breadcrumbs',
      '.dropdown','.dropdown-menu','.btn-group','[role="menu"]',
      '.modal','.popover'
    ].join(','),
    contentRoot: ['#gcAccountContent','#content','.gc-main-content','.page-content','.container','body'].join(','),
    doneClasses: ['user-state-accomplished','user-state-answered','user-state-finished','user-state-completed'],
    reachedClass: 'user-state-reached',
    missionClass: 'user-state-has_mission',
    checkIconSel: '.fa-check, .glyphicon-ok, .icon-check',
    maxDepth: 6,
    ttl: 20 * 60 * 1000,
    cacheAggKey: 'ted_sun_lite_agg_v1',
    cacheSnapKey: 'ted_sun_lite_snap_v1'
  };

  function clamp(n,a,b){ return Math.max(a, Math.min(b, n)); }
  function inBanned(el){ return el && el.closest && el.closest(CFG.bannedClosest); }
  function root(doc){ return (doc||document).querySelector(CFG.contentRoot) || (doc||document).body; }

  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 load(key){ try { return JSON.parse(localStorage.getItem(key) || '{}'); } catch(e){ return {}; } }
  function save(key, obj){ try { localStorage.setItem(key, JSON.stringify(obj)); } catch(e){} }
  function getCached(obj, id){
    var v = obj[id];
    if (!v) return null;
    if ((Date.now()-v.ts) > CFG.ttl) return null;
    return v.payload || null;
  }
  function setCached(obj, key, id, payload){
    obj[id] = { payload: payload, ts: Date.now() };
    save(key, obj);
  }

  function resolveHref(href, base){ try { return new URL(href, base).toString(); } catch(e){ return null; } }
  function canonTraining(url, base){
    try{
      var u = new URL(url, base||location.href); u.hash=''; u.search='';
      var m = u.pathname.match(CFG.trainingRe); if (m) u.pathname = m[0];
      return u.toString();
    }catch(e){
      var s = String(url||'').split('#')[0].split('?')[0];
      var m2 = s.match(CFG.trainingRe);
      return m2 ? m2[0] : s;
    }
  }
  function canonLesson(url, base){
    try{
      var u = new URL(url, base||location.href); u.hash=''; u.search='';
      var m = u.pathname.match(CFG.lessonRe); if (m) u.pathname = m[0];
      return u.toString();
    }catch(e){
      var s = String(url||'').split('#')[0].split('?')[0];
      var m2 = s.match(CFG.lessonRe);
      return m2 ? m2[0] : s;
    }
  }
  function idFrom(url){ var m = String(url).match(/\/id\/(\d+)/); return m ? m[1] : String(url); }

  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 stateCarrier(a){
    var c = a.closest ? (a.closest('li, tr, .lesson') || a) : a;
    var cn = String(c.className||'');
    if (cn.indexOf('user-state-') !== -1) return c;
    var inner = c.querySelector ? c.querySelector('[class*="user-state-"]') : null;
    return inner || c;
  }
  function isDone(carrier){
    if (!carrier) return false;
    if (hasAnyClass(carrier, CFG.doneClasses)) 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.checkIconSel)) return true;
    return false;
  }

  async function fetchDoc(url){
    var r = await fetch(url, { credentials:'include' });
    if (!r.ok) throw new Error('HTTP '+r.status);
    var html = await r.text();
    return new DOMParser().parseFromString(html,'text/html');
  }

  function lessonLinks(doc, base){
    var anchors = Array.from(root(doc).querySelectorAll('a[href]'));
    var out = [];
    anchors.forEach(function(a){
      if (inBanned(a)) return;
      var abs = resolveHref(a.getAttribute('href'), base); if (!abs) return;
      var c = canonLesson(abs, base);
      if (!CFG.lessonRe.test(c)) return;
      out.push({ a:a, url:c });
    });
    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, base){
    var links = lessonLinks(doc, base);
    var total = links.length, done = 0;
    links.forEach(function(x){
      if (isDone(stateCarrier(x.a))) done++;
    });
    done = clamp(done,0,total);
    return { done:done, total:total };
  }

  function childTrainings(doc, base){
    var anchors = Array.from(root(doc).querySelectorAll('a[href]'));
    var baseCanon = canonTraining(base, base);
    var baseId = idFrom(baseCanon);

    var byId = new Map();
    anchors.forEach(function(a){
      if (inBanned(a)) return;
      var abs = resolveHref(a.getAttribute('href'), base); if (!abs) return;
      var c = canonTraining(abs, base);
      if (!CFG.trainingRe.test(c)) return;
      var id = idFrom(c);
      if (id === baseId) return;

      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, c);
    });
    return Array.from(byId.values());
  }

  async function getTrainingsFromIndex(indexUrl){
    var doc = await fetchDoc(indexUrl);
    var anchors = Array.from(root(doc).querySelectorAll('a[href]'));
    var byId = new Map();
    anchors.forEach(function(a){
      if (inBanned(a)) return;
      var abs = resolveHref(a.getAttribute('href'), indexUrl); if (!abs) return;
      var c = canonTraining(abs, indexUrl);
      if (!CFG.trainingRe.test(c)) return;
      var id = idFrom(c);
      if (!byId.has(id)) byId.set(id, c);
    });
    return Array.from(byId.values());
  }

  async function snapshot(url, snapCache){
    var c = canonTraining(url, url);
    var id = idFrom(c);

    var cached = getCached(snapCache, id);
    if (cached) return cached;

    var doc = await fetchDoc(c);
    var ls = countLessons(doc, c);
    var kids = childTrainings(doc, c);
    var payload = { url:c, lessons:ls, children:kids };
    setCached(snapCache, CFG.cacheSnapKey, id, payload);
    return payload;
  }

  var inFlight = new Map();
  async function aggregate(url, depth, visited, aggCache, snapCache){
    var c = canonTraining(url, url);
    var id = idFrom(c);
    if (visited.has(id)) return { done:0, total:0 };
    visited.add(id);

    var cached = getCached(aggCache, id);
    if (cached) return cached;

    if (inFlight.has(id)) return await inFlight.get(id);

    var p = (async function(){
      try{
        var snap = await snapshot(c, snapCache);

        if (snap.lessons.total > 0) {
          var leaf = { done:snap.lessons.done, total:snap.lessons.total };
          setCached(aggCache, CFG.cacheAggKey, id, leaf);
          return leaf;
        }

        if (depth >= CFG.maxDepth || !snap.children.length){
          var empty = { done:0, total:0 };
          setCached(aggCache, CFG.cacheAggKey, id, empty);
          return empty;
        }

        var sum = { done:0, total:0 };
        for (var i=0;i<snap.children.length;i++){
          var r = await aggregate(snap.children[i], depth+1, visited, aggCache, snapCache);
          sum.done += r.done;
          sum.total += r.total;
        }
        setCached(aggCache, CFG.cacheAggKey, id, sum);
        return sum;
      } finally {
        inFlight.delete(id);
      }
    })();

    inFlight.set(id, p);
    return await p;
  }

  function icon(kind){
    if (kind === 'train') return '<svg width="18" height="18" viewBox="0 0 24 24" fill="none"><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"><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"><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 animateNumber(el, to){
    to = Number(to||0);
    var from = Number(el.getAttribute('data-prev') || el.textContent || 0);
    if (!isFinite(from)) from = 0;
    var start = performance.now();
    var dur = 520;

    function tick(t){
      var p = Math.min(1, (t-start)/dur);
      var ease = 1 - Math.pow(1-p, 3);
      el.textContent = String(Math.round(from + (to-from)*ease));
      if (p < 1) requestAnimationFrame(tick);
      else el.setAttribute('data-prev', String(to));
    }
    requestAnimationFrame(tick);
  }

  function setRing(host, done, total){
    var ring = host.querySelector('[data-kpi="ring"]');
    if (!ring) return;
    var r = Number(ring.getAttribute('r')) || 78;
    var circ = 2 * Math.PI * r;
    var p = total > 0 ? clamp(done/total, 0, 1) : 0;
    ring.style.strokeDasharray = String(circ);
    ring.style.strokeDashoffset = String(circ * (1 - p));
  }

  function render(host){
    host.innerHTML =
      '<div class="tsl-card">' +
      ' <div class="tsl-grid">' +
      '  <div class="tsl-orb">' +
      '   <svg class="tsl-ring" viewBox="0 0 200 200" aria-hidden="true">' +
      '    <circle cx="100" cy="100" r="78" stroke="rgba(15,23,42,.08)" stroke-width="12" fill="none"></circle>' +
      '    <circle data-kpi="ring" cx="100" cy="100" r="78" stroke="rgba(37,99,235,.95)" stroke-width="12" stroke-linecap="round" fill="none"></circle>' +
      '   </svg>' +
      '   <div class="tsl-center">' +
      '    <div class="tsl-num" data-kpi="done">0</div>' +
      '    <div class="tsl-label">пройдено</div>' +
      '    <div class="tsl-sub" data-kpi="sub">из 0 уроков</div>' +
      '   </div>' +
      '  </div>' +

      '  <div class="tsl-rays">' +
      '   <div class="tsl-ray tsl-train">' +
      '    <div class="tsl-ray__left"><div class="tsl-badge">'+icon('train')+'</div><div class="tsl-title">доступно тренингов</div></div>' +
      '    <div class="tsl-ray__right"><span class="tsl-val" data-kpi="t">0</span><span class="tsl-unit" data-kpi="tu">тренингов</span></div>' +
      '   </div>' +
      '   <div class="tsl-ray tsl-level">' +
      '    <div class="tsl-ray__left"><div class="tsl-badge">'+icon('level')+'</div><div class="tsl-title">доступно уровней</div></div>' +
      '    <div class="tsl-ray__right"><span class="tsl-val" data-kpi="l">0</span><span class="tsl-unit" data-kpi="lu">уровней</span></div>' +
      '   </div>' +
      '   <div class="tsl-ray tsl-less">' +
      '    <div class="tsl-ray__left"><div class="tsl-badge">'+icon('lesson')+'</div><div class="tsl-title">доступно уроков</div></div>' +
      '    <div class="tsl-ray__right"><span class="tsl-val" data-kpi="tot">0</span><span class="tsl-unit" data-kpi="uu">уроков</span></div>' +
      '   </div>' +
      '  </div>' +
      ' </div>' +
      ' <div class="tsl-foot">' +
      '  <div><b>Доступно:</b> <span data-kpi="footA">0</span></div>' +
      '  <div><b>Пройдено:</b> <span data-kpi="footD">0</span></div>' +
      ' </div>' +
      '</div>';
  }

  function update(host, s){
    var done = s.lessonsDone||0;
    var total = s.lessonsTotal||0;
    var t = s.trainingsTotal||0;
    var l = s.levelsTotal||0;

    var elDone = host.querySelector('[data-kpi="done"]');
    var elT = host.querySelector('[data-kpi="t"]');
    var elL = host.querySelector('[data-kpi="l"]');
    var elTot = host.querySelector('[data-kpi="tot"]');

    if (elDone) animateNumber(elDone, done);
    if (elT) animateNumber(elT, t);
    if (elL) animateNumber(elL, l);
    if (elTot) animateNumber(elTot, total);

    var sub = host.querySelector('[data-kpi="sub"]');
    if (sub) sub.textContent = 'из ' + total + ' ' + wLesson(total);

    var tu = host.querySelector('[data-kpi="tu"]');
    var lu = host.querySelector('[data-kpi="lu"]');
    var uu = host.querySelector('[data-kpi="uu"]');
    if (tu) tu.textContent = wTrain(t);
    if (lu) lu.textContent = wLevel(l);
    if (uu) uu.textContent = wLesson(total);

    var footA = host.querySelector('[data-kpi="footA"]');
    if (footA) footA.textContent = t + ' ' + wTrain(t) + ', ' + l + ' ' + wLevel(l) + ', ' + total + ' ' + wLesson(total);

    var footD = host.querySelector('[data-kpi="footD"]');
    if (footD) footD.textContent = done + ' ' + wLesson(done) + ' (из ' + total + ')';

    setRing(host, done, total);
  }

  function indexUrlFor(host){
    var explicit = host.getAttribute('data-index-url');
    if (explicit) { try { return new URL(explicit, location.origin).toString(); } catch(e){} }
    // fallback
    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 compute(host){
    var idx = indexUrlFor(host);
    var aggCache = load(CFG.cacheAggKey);
    var snapCache = load(CFG.cacheSnapKey);

    var trainings = [];
    try {
      trainings = await getTrainingsFromIndex(idx);
    } catch(e) {
      // если мы на странице тренинга — считаем хотя бы текущий
      if (CFG.trainingRe.test(location.pathname)) trainings = [canonTraining(location.href, location.href)];
      else throw e;
    }

    var s = { trainingsTotal: trainings.length, levelsTotal: 0, lessonsTotal: 0, lessonsDone: 0 };
    update(host, s);

    for (var i=0;i<trainings.length;i++){
      var tUrl = trainings[i];
      var agg = await aggregate(tUrl, 0, new Set(), aggCache, snapCache);
      s.lessonsTotal += agg.total;
      s.lessonsDone  += agg.done;

      var snap = await snapshot(tUrl, snapCache);
      s.levelsTotal += (snap.children || []).length;

      update(host, s);
    }

    update(host, s);
  }

  function init(){
    var nodes = Array.from(document.querySelectorAll(CFG.rootSel));
    nodes.forEach(function(host){
      render(host);
      update(host, { trainingsTotal:0, levelsTotal:0, lessonsTotal:0, lessonsDone:0 });
      compute(host).catch(function(){ /* тихо, чтобы GC не ругался */ });
    });
  }

  window.addEventListener('load', function(){ setTimeout(init, 200); });

  // Ops: можно дернуть в консоли, если цифры “залипли”
  window.TEDSunLite = {
    clearCache: function(){
      try { localStorage.removeItem(CFG.cacheAggKey); } catch(e){}
      try { localStorage.removeItem(CFG.cacheSnapKey); } catch(e){}
      console.log('[TEDSunLite] cache cleared');
    }
  };
})();
