(function () {
    'use strict';

    var CONFIG = {
        enabled: true,        // общий рубильник: false — скрипт молчит везде

        // Кому подсвечивать ответы: 'admins', 'users', 'all'
        audience: 'all',

        // Кому показывать правильный ответ в подсказке (title) у красного поля:
        //   'admins' | 'users' | 'all' | 'none'
        // Действует только там, где сработала сама подсветка (audience).
        showCorrect: 'all'
    };

    var AUDIENCES = ['admins', 'users', 'all'];
    var HINT_AUDIENCES = ['admins', 'users', 'all', 'none'];

    // Урок (браузер и webview приложения) или лента ответов. Префикс /pl бывает
    // и не бывает; домен не важен — morozilka.site тот же кабинет.
    function matchPage(pathname) {
        if (/^\/(pl\/)?teach\/control\/lesson\/(view|webview)(\/|$)/.test(pathname)) return 'lesson';
        if (/^\/(pl\/)?teach\/control\/answers(\/|$)/.test(pathname)) return 'feed';
        if (/^\/(pl\/)?teach\/control\/stat\/userComments(\/|$)/.test(pathname)) return 'user';
        return null;
    }

    var ANY_ORDER = /\(\s*любой\s+порядок\s*\)/i;

    // Текст HTML-комментария -> варианты ответа, или null, если это не метка.
    // Вариант со значком ~ — «символы в любом порядке». Приписка «(любой порядок)»
    // ставит этот значок всем вариантам метки.
    function parseKey(text) {
        var m = /^\s*(?:ответ\s*:|#)([\s\S]*)$/i.exec(String(text));
        if (!m) return null;
        var body = m[1];
        var anyOrder = ANY_ORDER.test(body);
        var variants = body.replace(ANY_ORDER, '').split('|')
            .map(function (v) { return v.trim(); })
            .filter(function (v) { return v !== '' && v !== '~'; })
            .map(function (v) { return anyOrder && v.charAt(0) !== '~' ? '~' + v : v; });
        return variants.length ? variants : null;
    }

    // Регистр, пробелы, запятая/точка, вид тире, ё/е и нули в конце дробной части
    // не важны. Ведущие нули важны: 0110 и 110 — разные ответы-коды.
    function normalize(value) {
        var s = String(value).toLowerCase()
            .replace(/\s+/g, '')
            .replace(/,/g, '.')
            .replace(/[\u2010-\u2015\u2212\uFE58\uFE63\uFF0D]/g, '-')   // все тире и минусы
            .replace(/ё/g, 'е');
        if (/^-?\d+\.\d*$/.test(s)) s = s.replace(/0+$/, '').replace(/\.$/, '');
        return s;
    }

    // «В любом порядке»: тот же набор символов, разделители , . ; не важны
    function sortedChars(value) {
        return normalize(value).replace(/[.;]/g, '').split('').sort().join('');
    }

    function isCorrect(value, variants) {
        var v = normalize(value);
        return variants.some(function (x) {
            if (x.charAt(0) === '~') return sortedChars(value) === sortedChars(x.slice(1));
            return normalize(x) === v;
        });
    }

    // true/false — пускать или нет; null — значение рубильника не из списка
    function allowed(mode, staff, modes) {
        if (modes.indexOf(mode) === -1) return null;
        if (mode === 'all') return true;
        if (mode === 'admins') return staff;
        if (mode === 'users') return !staff;
        return false; // 'none'
    }

    function hintText(variants) {
        return 'Правильный ответ: ' + variants.map(function (x) {
            return x.charAt(0) === '~' ? x.slice(1) + ' (в любом порядке)' : x;
        }).join(' или ');
    }

    // ---------- браузер ----------

    var STYLE_ID = 'gc-answer-keys-style';
    var MARK = 'data-answer-check';

    function isStaff() {
        var info = window.userInfo;
        return !!(info && (info.isAdmin || info.isTeacher || info.isManager));
    }

    // Первая метка среди комментариев внутри <label> поля, на любой глубине
    function findKey(field) {
        var label = field.querySelector('label');
        if (!label) return null;
        var walker = document.createTreeWalker(label, NodeFilter.SHOW_COMMENT);
        for (var node = walker.nextNode(); node; node = walker.nextNode()) {
            var key = parseKey(node.data);
            if (key) return key;
        }
        return null;
    }

    // Ответ — .addfield.type-string со значением в .value.js__to-remove-<id>.
    // Поля формы — .addfield-type-string с input — сюда не попадают.
    // Поле с меткой размечаем сами, снимая классы старого блока correctAnswers.
    function markField(field, showHint) {
        var valueEl = field.querySelector('.value[class*="js__to-remove-"]');
        var key = valueEl ? findKey(field) : null;
        if (!key) {
            field.setAttribute(MARK, 'none');
            return;
        }
        var ok = isCorrect(valueEl.textContent.trim(), key);
        field.classList.remove('correct-answer', 'incorrect-answer');
        field.classList.add(ok ? 'correct-answer' : 'incorrect-answer');
        field.setAttribute(MARK, ok ? 'correct' : 'incorrect');
        if (showHint && !ok) valueEl.setAttribute('title', hintText(key));
    }

    function scan(showHint) {
        var fields = document.querySelectorAll('.addfield.type-string:not([' + MARK + '])');
        for (var i = 0; i < fields.length; i++) markField(fields[i], showHint);
    }

    // Полоса слева, ответ цветом и значком, вопрос приглушён — страница однотоннее,
    // ответ виден сразу. Селекторы .addfield.* сильнее, чем .correct-answer у старых
    // блоков correctAnswers, поэтому их поля выглядят так же.
    var OK = '.addfield.correct-answer', NO = '.addfield.incorrect-answer';
    function both(tail) { return OK + tail + ',' + NO + tail; }

    function injectStyles() {
        if (document.getElementById(STYLE_ID)) return;
        var style = document.createElement('style');
        style.id = STYLE_ID;
        style.textContent = [
            both('') + '{background:#f8f9fa!important;border:0!important;border-left:5px solid!important;' +
                'border-radius:6px!important;padding:10px 12px!important}',
            OK + '{border-left-color:#28a745!important}',
            NO + '{border-left-color:#dc3545!important}',
            both(' label') + ',' + both(' label p') + '{font-weight:400!important;color:#5f6368!important}',
            both(' label b') + '{font-weight:700!important;color:#333!important}',
            both(' .value') + '{font-weight:700!important;font-size:1.25em!important;margin-top:6px!important}',
            OK + ' .value{color:#1e7e34!important}',
            NO + ' .value{color:#c82333!important}',
            OK + ' .value::before{content:"✓ "}',
            NO + ' .value::before{content:"✗ "}'
        ].join('\n');
        document.head.appendChild(style);
    }

    // Лента догружает ответы, вкладка ответов перерисовывается после отправки.
    // Пересканируем не чаще раза за кадр; свои правки — атрибуты, не узлы, цикла нет.
    function watch(showHint) {
        var queued = false;
        new MutationObserver(function (mutations) {
            if (queued) return;
            for (var i = 0; i < mutations.length; i++) {
                if (mutations[i].addedNodes.length) {
                    queued = true;
                    requestAnimationFrame(function () {
                        queued = false;
                        scan(showHint);
                    });
                    return;
                }
            }
        }).observe(document.body, { childList: true, subtree: true });
    }

    function init() {
        if (window.gcAnswerKeysStarted) return; // скрипт подключён повторно
        var staff = isStaff();
        var show = allowed(CONFIG.audience, staff, AUDIENCES);
        if (show === null) {
            console.warn('[gc-answer-keys] неизвестное значение audience:', CONFIG.audience);
            return;
        }
        if (!show) return;
        var hint = allowed(CONFIG.showCorrect, staff, HINT_AUDIENCES);
        if (hint === null) {
            console.warn('[gc-answer-keys] неизвестное значение showCorrect:', CONFIG.showCorrect);
            hint = false;
        }
        window.gcAnswerKeysStarted = true;
        injectStyles();
        scan(hint);
        watch(hint);
    }

    if (typeof module !== 'undefined' && module.exports) {
        // Node: только чистые функции, для тестов
        module.exports = { matchPage, parseKey, normalize, sortedChars, isCorrect, allowed, hintText, AUDIENCES, HINT_AUDIENCES };
    } else if (CONFIG.enabled && matchPage(location.pathname)) {
        // На верхнем уровне — только location; DOM — после разбора документа
        if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
        else init();
    }
})();