(function () {
  'use strict';

  if (localStorage.getItem('tedProgressOff') === '1') return;
  if (window.__TED_PROGRESS_LISTONLY_V82__) return;
  window.__TED_PROGRESS_LISTONLY_V82__ = true;

  var CFG = {
    maxDepth: 4,
    fetchConcurrency: 1,
    cacheTtlMs: 20 * 60 * 1000,
    cacheKey: 'ted_training_progress_cache_listonly_v82',
    ioRootMargin: '500px 0px',

    trainingPathRe: /\/teach\/control\/stream\/view\/id\/\d+/,
    lessonPathRe: /\/teach\/control\/lesson\/view\/id\/\d+/,

    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',

    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(','),

    text: {
      calculating: 'Считаю…',
      noLessons: 'Уроков пока нет',
      doneTraining: 'Тренинг завершён',
      doneLesson: 'Урок завершён',
      folderDone: 'Все уровни завершены',
      folderEmpty: 'Нет уровней',
      leftPrefix: 'Осталось: ',
      donePrefix: 'Пройдено: '
    },

    debug: false
  };

  function log(){ if(CFG.debug) console.log.apply(console, arguments); }
  function clamp(n,min,max){ return Math.max(min, Math.min(max, n)); }

  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 lessonWord(n){ return pluralRu(n,'урок','урока','уроков'); }
  function levelWord(n){ return pluralRu(n,'уровень','уровня','уровней'); }

  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] : url;
  }
  function resolveHref(href, baseUrl){
    try { return new URL(href, baseUrl).toString(); } catch(e){ return null; }
  }

  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 injectCss(){
    if (document.getElementById('ted-trprogress-style')) return;
    var css =
      '.ted-trprogress{margin:8px 0 0;max-width:760px;padding:10px 12px;background:#fff;border:1px solid rgba(15,23,42,.10);border-radius:12px;box-shadow:0 6px 16px rgba(15,23,42,.05)}' +
      '.ted-trprogress__top{display:flex;align-items:baseline;justify-content:space-between;gap:12px;font-size:12px;color:#64748B}' +
      '.ted-trprogress__nums{font-weight:750;color:#0F172A;white-space:nowrap}' +
      '.ted-trprogress__bar{margin-top:8px;height:10px;background:rgba(15,23,42,.08);border-radius:999px;overflow:hidden}' +
      '.ted-trprogress__bar>i{display:block;height:100%;width:0%;background:linear-gradient(90deg,#4F46E5,#06B6D4);transition:width .6s ease;border-radius:999px}';
    var st = document.createElement('style');
    st.id = 'ted-trprogress-style';
    st.textContent = css;
    document.head.appendChild(st);
  }

  function loadCache(){
    try { return JSON.parse(localStorage.getItem(CFG.cacheKey) || '{}'); }
    catch(e){ return {}; }
  }
  function saveCache(c){
    try { localStorage.setItem(CFG.cacheKey, JSON.stringify(c)); } catch(e){}
  }
  function getCached(c, id){
    var v = c[id];
    if (!v) return null;
    if ((Date.now() - v.ts) > CFG.cacheTtlMs) return null;
    return v.payload || null;
  }
  function setCached(c, id, payload){
    c[id] = { payload: payload, ts: Date.now() };
    saveCache(c);
  }

  // &#9989; FIXED cleanup: удаляем ТОЛЬКО наши бары, и только если они попали в запрещённые зоны
  function cleanup(){
    // удаляем только старый верхний бар, если он был от предыдущих версий
    var top = document.getElementById('ted-current-training-progress');
    if (top && top.classList && top.classList.contains('ted-trprogress')) {
      try { top.remove(); } catch(e){}
    }

    // удаляем наши бары, если они оказались внутри меню/сайдбара/хедера и т.д.
    var all = document.querySelectorAll('.ted-trprogress');
    Array.from(all).forEach(function(el){
      if (el && el.closest && el.closest(CFG.bannedClosestSelector)) {
        try { el.remove(); } catch(e){}
      }
    });
  }

  function makeUI(){
    var wrap = document.createElement('div');
    wrap.className = 'ted-trprogress';
    wrap.innerHTML =
      '<div class="ted-trprogress__top">' +
      '  <span class="ted-trprogress__state">' + CFG.text.calculating + '</span>' +
      '  <span class="ted-trprogress__nums">—</span>' +
      '</div>' +
      '<div class="ted-trprogress__bar"><i></i></div>';
    return wrap;
  }
  function setCalculating(ui){
    ui.querySelector('.ted-trprogress__state').textContent = CFG.text.calculating;
    ui.querySelector('.ted-trprogress__nums').textContent = '—';
    ui.querySelector('.ted-trprogress__bar > i').style.width = '0%';
  }
  function renderLeaf(ui, done, total){
    done = Math.max(0, done);
    total = Math.max(0, total);
    if (total > 0) done = clamp(done, 0, total);

    var left = Math.max(0, total - done);
    var pct = total > 0 ? (done / total) * 100 : 0;
    pct = clamp(pct, 0, 100);

    if (total === 0) {
      ui.querySelector('.ted-trprogress__state').textContent = CFG.text.noLessons;
      ui.querySelector('.ted-trprogress__nums').textContent = '—';
      ui.querySelector('.ted-trprogress__bar > i').style.width = '0%';
      return;
    }

    ui.querySelector('.ted-trprogress__nums').textContent =
      CFG.text.donePrefix + done + ' ' + lessonWord(done) + ' из ' + total;

    ui.querySelector('.ted-trprogress__state').textContent =
      left === 0 ? (total === 1 ? CFG.text.doneLesson : CFG.text.doneTraining)
                : (CFG.text.leftPrefix + left + ' ' + lessonWord(left));

    ui.querySelector('.ted-trprogress__bar > i').style.width = pct.toFixed(2) + '%';
  }
  function renderFolder(ui, doneLevels, totalLevels){
    doneLevels = Math.max(0, doneLevels);
    totalLevels = Math.max(0, totalLevels);
    if (totalLevels > 0) doneLevels = clamp(doneLevels, 0, totalLevels);

    var left = Math.max(0, totalLevels - doneLevels);
    var pct = totalLevels > 0 ? (doneLevels / totalLevels) * 100 : 0;
    pct = clamp(pct, 0, 100);

    if (totalLevels === 0) {
      ui.querySelector('.ted-trprogress__state').textContent = CFG.text.folderEmpty;
      ui.querySelector('.ted-trprogress__nums').textContent = '—';
      ui.querySelector('.ted-trprogress__bar > i').style.width = '0%';
      return;
    }

    ui.querySelector('.ted-trprogress__nums').textContent =
      CFG.text.donePrefix + doneLevels + ' ' + levelWord(doneLevels) + ' из ' + totalLevels;

    ui.querySelector('.ted-trprogress__state').textContent =
      left === 0 ? CFG.text.folderDone
                : (CFG.text.leftPrefix + left + ' ' + levelWord(left));

    ui.querySelector('.ted-trprogress__bar > i').style.width = pct.toFixed(2) + '%';
  }

  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 { total: 0, done: 0 };

    var total = links.length, done = 0;
    links.forEach(function(x){
      var host = getStateCarrier(x.a);
      if (isLessonDone(host)) done++;
    });
    done = clamp(done, 0, total);
    return { total: total, done: done };
  }

  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;

      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');
  }

  function isComplete(info){
    if (!info) return false;
    if (info.type === 'leaf') return info.totalLessons > 0 && info.doneLessons === info.totalLessons;
    if (info.type === 'folder') return info.totalLevels > 0 && info.doneLevels === info.totalLevels;
    return false;
  }

  var inFlight = new Map();

  async function analyzeTraining(url, depth, visited, cache){
    var canonUrl = canonicalTrainingUrl(url, url);
    var id = idFromUrl(canonUrl);

    if (visited.has(id)) return { type:'empty', doneLessons:0, totalLessons:0, doneLevels:0, totalLevels:0 };
    visited.add(id);

    var cached = getCached(cache, id);
    if (cached) return cached;

    if (inFlight.has(id)) return await inFlight.get(id);

    var p = (async function(){
      try{
        var doc = await fetchDoc(canonUrl);

        var ls = countLessons(doc, canonUrl);
        if (ls.total > 0) {
          var leaf = { type:'leaf', doneLessons: ls.done, totalLessons: ls.total, doneLevels:0, totalLevels:0 };
          setCached(cache, id, leaf);
          return leaf;
        }

        if (depth < CFG.maxDepth) {
          var kids = extractChildTrainings(doc, canonUrl);
          if (kids.length > 0) {
            var doneLevels = 0;
            for (var i=0;i<kids.length;i++){
              var child = await analyzeTraining(kids[i], depth+1, visited, cache);
              if (isComplete(child)) doneLevels++;
            }
            var folder = { type:'folder', doneLessons:0, totalLessons:0, doneLevels: doneLevels, totalLevels: kids.length };
            setCached(cache, id, folder);
            return folder;
          }
        }

        var empty = { type:'empty', doneLessons:0, totalLessons:0, doneLevels:0, totalLevels:0 };
        setCached(cache, id, empty);
        return empty;

      } finally {
        inFlight.delete(id);
      }
    })();

    inFlight.set(id, p);
    return await p;
  }

  var queue = [];
  var active = 0;

  function enqueue(job){
    queue.push(job);
    pump();
  }
  function pump(){
    if (active >= CFG.fetchConcurrency) return;
    var job = queue.shift();
    if (!job) return;
    active++;

    (async function(){
      try{
        var cache = loadCache();
        var visited = new Set();
        var info = await analyzeTraining(job.url, 0, visited, cache);

        if (!info || info.type === 'empty') renderLeaf(job.ui, 0, 0);
        else if (info.type === 'leaf') renderLeaf(job.ui, info.doneLessons, info.totalLessons);
        else if (info.type === 'folder') renderFolder(job.ui, info.doneLevels, info.totalLevels);
        else renderLeaf(job.ui, 0, 0);

      } catch(e){
        log('[TED LISTONLY v8.2] error', job.url, e);
      } finally {
        active--;
        pump();
      }
    })();
  }

  function scoreAnchor(a){
    var s = 0;
    if (inBannedUI(a)) return -99999;
    if (a.closest && a.closest('h1,h2,h3,h4')) s += 200;
    if (a.closest && a.closest('li, tr')) s += 80;
    var t = (a.textContent || '').trim();
    if (t.length >= 3) s += Math.min(50, t.length);
    return s;
  }

  function mountListWidgets(io){
    var root = getContentRoot(document);
    var anchors = Array.from(root.querySelectorAll('a[href]'));

    var currentId = CFG.trainingPathRe.test(location.pathname)
      ? idFromUrl(canonicalTrainingUrl(location.href, location.href))
      : null;

    var bestById = new Map();

    anchors.forEach(function(a){
      if (inBannedUI(a)) return;

      var abs = resolveHref(a.getAttribute('href'), location.href);
      if (!abs) return;

      var canon = canonicalTrainingUrl(abs, location.href);
      if (!CFG.trainingPathRe.test(canon)) return;

      var id = idFromUrl(canon);
      if (currentId && id === currentId) return;

      // не цепляем кнопки и элементы управления
      if (a.closest && a.closest('.btn, .button, .btn-group, .dropdown, .dropdown-menu')) return;

      var sc = scoreAnchor(a);
      var prev = bestById.get(id);
      if (!prev || sc > prev.sc) bestById.set(id, { a:a, url:canon, sc:sc });
    });

    bestById.forEach(function(x){
      var a = x.a;
      var url = x.url;

      var next = a.nextElementSibling;
      var ui = (next && next.classList && next.classList.contains('ted-trprogress')) ? next : null;

      if (!ui) {
        ui = makeUI();
        a.insertAdjacentElement('afterend', ui);
      }
      setCalculating(ui);

      ui.__tedUrl = url;
      io.observe(ui);
    });
  }

  function setupIO(){
    if (!('IntersectionObserver' in window)) return null;
    return new IntersectionObserver(function(entries){
      entries.forEach(function(e){
        if (!e.isIntersecting) return;
        var ui = e.target;
        io.unobserve(ui);
        if (!ui.__tedUrl) return;
        enqueue({ url: ui.__tedUrl, ui: ui });
      });
    }, { root:null, rootMargin: CFG.ioRootMargin, threshold: 0.01 });
  }

  var io = null;

  function init(){
    injectCss();
    cleanup();

    io = setupIO();
    if (!io) {
      mountListWidgets({ observe: function(ui){ enqueue({ url: ui.__tedUrl, ui: ui }); } });
      return;
    }
    mountListWidgets(io);
  }

  window.addEventListener('load', function(){
    setTimeout(function(){
      try { init(); } catch(e) { console.warn('[TED LISTONLY v8.2] init failed', e); }
    }, 600);
  });

  window.TEDTrainingProgressListOnly = {
    off: function(){ localStorage.setItem('tedProgressOff','1'); location.reload(); },
    on: function(){ localStorage.removeItem('tedProgressOff'); location.reload(); },
    clearCache: function(){ try { localStorage.removeItem(CFG.cacheKey); } catch(e){} console.log('[TED LISTONLY v8.2] cache cleared'); }
  };

})();
