/**
 * autotext.js — ตัวช่วยเติมข้อความอัตโนมัติในหน้า procedure
 *
 * เขียนด้วย vanilla JS ล้วน ๆ โดยตั้งใจ:
 *   - ไม่พึ่ง jQuery  -> หน้าไหนโหลด jQuery ซ้ำก็ไม่ทำให้โมดูลนี้หาย (บั๊กเดิมเกิดจาก jQuery ตัวที่สองทับ $.fn.inputDropdown ทิ้ง)
 *   - ไม่พึ่ง stylesheet ภายนอก -> ฉีด CSS ของตัวเองตอนเริ่ม
 *   - ใช้ event delegation -> ช่องที่ถูกสร้างทีหลังด้วย ajax/Livewire ใช้งานได้ทันที
 *
 * ตั้งค่าผ่าน window.AUTOTEXT_CONFIG ก่อนโหลดไฟล์นี้:
 *   { endpoint, procedure, enabled, csrf }
 *
 * ปุ่ม × ไม่ได้ลบข้อมูล แต่ส่ง event=hideautotext ให้ server ติดธง hidden ไว้
 *
 * API ฝั่ง server เหมือนเดิมทุกอย่าง: POST <endpoint> event=jqinputdropdown
 */
(function () {
    'use strict';

    var CONFIG = window.AUTOTEXT_CONFIG || {};
    if (!CONFIG.enabled || !CONFIG.endpoint) { return; }

    var MIN_CHARS     = 4;     // ต้องพิมพ์อย่างน้อยกี่ตัวก่อนถึงจะขึ้นคำแนะนำ
    var DEBOUNCE_MS   = 180;   // หน่วงก่อนยิง เพื่อไม่ให้ทุก keystroke กลายเป็น query
    var MAX_RENDER    = 50;    // บางฟิลด์มีคำที่จำไว้หลายร้อยรายการ แสดงเท่าที่ใช้จริงพอ
    var CACHE_LIMIT   = 300;
    var MIN_WIDTH_PX  = 180;
    var NOTE_MS       = 2000;  // เวลาแสดงข้อความแจ้งผลการลบ

    /* ---------- กติกาว่าช่องไหนใช้ key อะไรไปถาม server ---------- */
    /* เรียงจากเฉพาะเจาะจงมากสุดไปน้อยสุด เพราะ element เดียวอาจเข้าได้หลายข้อ */
    var RULES = [
        {
            selector: '.photo_autotext',
            key: function (el) {
                var photoId = el.getAttribute('photoid');
                if (!photoId) { return null; }
                var select = document.querySelector('select[photo_id="' + cssEscape(photoId) + '"]');
                return 'mainsub' + (select ? select.value : '');
            }
        },
        {
            selector: '.autotextgroup',
            key: function (el) { return el.getAttribute('group'); }
        },
        {
            selector: '.autotext',
            key: function (el) { return el.id; }
        }
    ];

    /* ---------- state ---------- */
    var box        = null;   // dropdown ตัวเดียว ใช้ซ้ำทุกช่อง (ของเดิม append <ul> ใหม่ทุก keystroke จน DOM รั่วและ id ซ้ำ)
    var activeEl   = null;
    var activeKey  = '';     // key ที่ใช้ถาม server ของช่องปัจจุบัน — ต้องใช้ตอนสั่งลบคำแนะนำด้วย
    var items      = [];
    var activeIdx  = -1;
    var noteId     = null;
    var debounceId = null;
    var inflight   = null;   // AbortController ของ request ล่าสุด
    var cache      = new Map();

    /* ---------- utils ---------- */

    function cssEscape(value) {
        if (window.CSS && CSS.escape) { return CSS.escape(value); }
        return String(value).replace(/["\\]/g, '\\$&');
    }

    function isEditable(el) {
        return el && (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA');
    }

    /** หา element ที่ควรทำงานด้วย พร้อม key สำหรับถาม server */
    function resolve(target) {
        for (var i = 0; i < RULES.length; i++) {
            var el = target.closest ? target.closest(RULES[i].selector) : null;
            if (!el || !isEditable(el)) { continue; }   // .autotext ถูกใส่บน <span> บางที่ด้วย จึงต้องกรอง
            var key = RULES[i].key(el);
            if (key) { return { el: el, key: key }; }
        }
        return null;
    }

    function injectStyle() {
        if (document.getElementById('autotext-style')) { return; }
        var style = document.createElement('style');
        style.id = 'autotext-style';
        style.textContent = [
            '.autotext-dropdown{position:fixed;z-index:2147483000;display:none;margin:0;padding:4px 0;',
            'list-style:none;background:#fff;color:#212529;border:1px solid #ced4da;border-radius:4px;',
            'box-shadow:0 4px 14px rgba(0,0,0,.18);max-height:260px;overflow-y:auto;font-size:14px;',
            'line-height:1.4;box-sizing:border-box}',
            '.autotext-dropdown li{padding:6px 12px;cursor:pointer;white-space:pre-wrap;word-break:break-word;',
            'display:flex;align-items:flex-start;gap:8px}',
            '.autotext-dropdown li:hover,.autotext-dropdown li.is-active{background:#0d6efd;color:#fff}',
            '.autotext-dropdown .autotext-text{flex:1 1 auto;min-width:0}',
            '.autotext-dropdown .autotext-del{flex:0 0 auto;width:20px;height:20px;line-height:18px;',
            'text-align:center;border-radius:50%;font-size:16px;color:#adb5bd;cursor:pointer;',
            'user-select:none}',
            '.autotext-dropdown li:hover .autotext-del,.autotext-dropdown li.is-active .autotext-del{color:#fff}',
            '.autotext-dropdown .autotext-del:hover{background:#dc3545;color:#fff}',
            '.autotext-dropdown li.autotext-empty{cursor:default;color:#6c757d;font-style:italic;display:block}',
            '.autotext-dropdown li.autotext-empty:hover{background:transparent;color:#6c757d}',
            '.autotext-dropdown li.autotext-hint{font-size:12px;font-style:normal;border-top:1px solid #e9ecef;',
            'padding-top:5px;margin-top:2px;color:#868e96}',
            '.autotext-dropdown li.autotext-note{font-style:normal;color:#0a58ca;background:#e7f1ff}'
        ].join('');
        document.head.appendChild(style);
    }

    function ensureBox() {
        if (box) { return box; }
        injectStyle();
        box = document.createElement('ul');
        box.className = 'autotext-dropdown';
        box.setAttribute('role', 'listbox');
        // mousedown แทน click เพราะ click จะมาหลัง blur ซึ่งปิด dropdown ไปแล้ว
        box.addEventListener('mousedown', function (e) {
            var li = e.target.closest('li[data-value]');
            if (!li) { return; }
            e.preventDefault();

            if (e.target.closest('.autotext-del')) {
                hideValue(li.getAttribute('data-value'));
            } else {
                choose(li.getAttribute('data-value'));
            }
        });
        // ชี้เมาส์แล้วให้ไฮไลต์รายการนั้น (สอดคล้องกับการเลือกด้วย ↑ ↓)
        box.addEventListener('mouseover', function (e) {
            var li = e.target.closest('li[data-value]');
            if (!li) { return; }
            var all = [].slice.call(box.querySelectorAll('li[data-value]'));
            highlight(all.indexOf(li));
        });
        document.body.appendChild(box);
        return box;
    }

    function position() {
        if (!activeEl || !box) { return; }
        var rect  = activeEl.getBoundingClientRect();
        var width = Math.max(rect.width, MIN_WIDTH_PX);

        box.style.left  = rect.left + 'px';
        box.style.width = width + 'px';

        // ถ้าที่ว่างด้านล่างไม่พอ ให้เปิดขึ้นด้านบนแทน
        var below = window.innerHeight - rect.bottom;
        if (below < 160 && rect.top > below) {
            box.style.top       = 'auto';
            box.style.bottom    = (window.innerHeight - rect.top + 2) + 'px';
            box.style.maxHeight = Math.min(260, rect.top - 8) + 'px';
        } else {
            box.style.bottom    = 'auto';
            box.style.top       = (rect.bottom + 2) + 'px';
            box.style.maxHeight = Math.min(260, below - 8) + 'px';
        }
    }

    function close() {
        if (box) { box.style.display = 'none'; }
        activeEl  = null;
        activeKey = '';
        items     = [];
        activeIdx = -1;
        if (inflight) { inflight.abort(); inflight = null; }
        clearTimeout(debounceId);
        clearTimeout(noteId);
    }

    /** ข้อความแจ้งผลชั่วคราวที่หัว dropdown (เช่น ลบคำแนะนำแล้ว) */
    function showNote(text) {
        ensureBox();
        clearTimeout(noteId);

        var old = box.querySelector('.autotext-note');
        if (old) { old.parentNode.removeChild(old); }

        var li = document.createElement('li');
        li.className = 'autotext-empty autotext-note';
        li.textContent = text;
        box.insertBefore(li, box.firstChild);
        box.style.display = 'block';
        position();

        noteId = setTimeout(function () {
            if (li.parentNode) { li.parentNode.removeChild(li); }
            if (!box.querySelector('li[data-value]')) { close(); }
        }, NOTE_MS);
    }

    function render(list) {
        ensureBox();
        items     = list.slice(0, MAX_RENDER);
        activeIdx = -1;

        if (!items.length) { close(); return; }

        var html = '';
        for (var i = 0; i < items.length; i++) {
            html += '<li role="option" data-value="' + escapeAttr(items[i]) + '">'
                  +     '<span class="autotext-text">' + escapeHtml(items[i]) + '</span>'
                  +     '<span class="autotext-del" role="button" title="ไม่ต้องแนะนำคำนี้อีก (ข้อมูลยังเก็บไว้)">&times;</span>'
                  + '</li>';
        }
        if (list.length > MAX_RENDER) {
            html += '<li class="autotext-empty">… อีก ' + (list.length - MAX_RENDER) + ' รายการ (พิมพ์เพิ่มเพื่อกรอง)</li>';
        }
        html += '<li class="autotext-empty autotext-hint">คลิกเพื่อใช้ข้อความ · กด × เพื่อไม่ให้แนะนำคำนั้นอีก</li>';
        box.innerHTML = html;
        box.style.display = 'block';
        box.scrollTop = 0;
        position();
    }

    function escapeHtml(s) {
        return String(s).replace(/[&<>]/g, function (c) {
            return { '&': '&amp;', '<': '&lt;', '>': '&gt;' }[c];
        });
    }

    function escapeAttr(s) {
        return escapeHtml(s).replace(/"/g, '&quot;');
    }

    function highlight(idx) {
        var lis = box.querySelectorAll('li[data-value]');
        for (var i = 0; i < lis.length; i++) { lis[i].classList.remove('is-active'); }
        if (idx >= 0 && lis[idx]) {
            lis[idx].classList.add('is-active');
            lis[idx].scrollIntoView({ block: 'nearest' });
        }
        activeIdx = idx;
    }

    function choose(value) {
        if (!activeEl) { return; }
        var el = activeEl;
        el.value = value;
        // ให้ตัวฟังอื่น ๆ (เช่น .savejson ที่บันทึกตอน focusout) เห็นค่าที่เปลี่ยน
        el.dispatchEvent(new Event('input',  { bubbles: true }));
        el.dispatchEvent(new Event('change', { bubbles: true }));
        close();
        el.focus();
    }

    /**
     * ซ่อนคำแนะนำ (กดปุ่ม × ท้ายรายการ)
     *
     * ไม่ได้ลบข้อมูลทิ้ง — ฝั่ง server แค่ติดธง hidden ไว้ จึงกู้คืนได้ภายหลัง
     */
    function hideValue(value) {
        if (!activeEl || !value) { return; }

        var key = activeKey;

        var body = new URLSearchParams();
        body.set('event', 'hideautotext');
        body.set('text', value);
        body.set('textid', key);
        body.set('procedure', CONFIG.procedure || '');

        var headers = {
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
            'X-Requested-With': 'XMLHttpRequest'
        };
        if (CONFIG.csrf) { headers['X-CSRF-TOKEN'] = CONFIG.csrf; }

        fetch(CONFIG.endpoint, {
            method: 'POST',
            headers: headers,
            body: body,
            credentials: 'same-origin'
        }).then(function (res) {
            if (!res.ok) { throw new Error('HTTP ' + res.status); }
            cache.clear();                       // คำที่ซ่อนแล้วต้องไม่ถูกหยิบจาก cache มาแสดงอีก
            var left = items.filter(function (v) { return v !== value; });
            if (left.length) { render(left); }
            else { box.innerHTML = ''; items = []; activeIdx = -1; }
            showNote('เอา "' + value + '" ออกจากคำแนะนำแล้ว (ข้อมูลยังเก็บไว้)');
        }).catch(function (err) {
            console.warn('[autotext] เอาคำแนะนำออกไม่สำเร็จ:', err);
            showNote('ทำรายการไม่สำเร็จ ลองใหม่อีกครั้ง');
        });
    }

    /* ---------- ยิงขอข้อมูล ---------- */

    function lookup(key, value) {
        var cacheKey = key + '\u0000' + CONFIG.procedure + '\u0000' + value;
        if (cache.has(cacheKey)) { return Promise.resolve(cache.get(cacheKey)); }

        if (inflight) { inflight.abort(); }
        inflight = new AbortController();

        var body = new URLSearchParams();
        body.set('event', 'jqinputdropdown');
        body.set('textid', key);
        body.set('value', value);
        body.set('procedure', CONFIG.procedure || '');

        var headers = {
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
            'X-Requested-With': 'XMLHttpRequest'
        };
        if (CONFIG.csrf) { headers['X-CSRF-TOKEN'] = CONFIG.csrf; }

        return fetch(CONFIG.endpoint, {
            method: 'POST',
            headers: headers,
            body: body,
            credentials: 'same-origin',
            signal: inflight.signal
        }).then(function (res) {
            return res.ok ? res.json() : [];
        }).then(function (rows) {
            var values = (Array.isArray(rows) ? rows : [])
                .map(function (r) { return r && r.value; })
                .filter(function (v) { return typeof v === 'string' && v !== ''; });

            if (cache.size >= CACHE_LIMIT) { cache.clear(); }
            cache.set(cacheKey, values);
            return values;
        });
    }

    function request(el, key) {
        activeEl  = el;
        activeKey = key;
        clearTimeout(debounceId);
        var value = el.value;

        // ต้องพิมพ์ครบ MIN_CHARS ตัวก่อน ถึงจะเริ่มแนะนำ
        if (value.trim().length < MIN_CHARS) {
            clearTimeout(debounceId);
            if (inflight) { inflight.abort(); inflight = null; }
            if (box) { box.style.display = 'none'; }
            items     = [];
            activeIdx = -1;
            return;
        }

        debounceId = setTimeout(function () {
            lookup(key, value).then(function (values) {
                if (activeEl !== el) { return; }   // ผู้ใช้ย้ายไปช่องอื่นแล้ว ทิ้งผลนี้
                render(values);
            }).catch(function (err) {
                if (err && err.name === 'AbortError') { return; }
                console.warn('[autotext] ดึงข้อมูลไม่สำเร็จ:', err);
                close();
            });
        }, DEBOUNCE_MS);
    }

    /* ---------- events ---------- */

    document.addEventListener('input', function (e) {
        var hit = resolve(e.target);
        if (!hit) { return; }
        request(hit.el, hit.key);
    });

    document.addEventListener('focusin', function (e) {
        var hit = resolve(e.target);
        if (!hit) { close(); return; }
        request(hit.el, hit.key);
    });

    document.addEventListener('focusout', function (e) {
        if (!activeEl || e.target !== activeEl) { return; }
        // หน่วงนิดเดียวเพื่อให้ mousedown บนรายการทำงานก่อน
        setTimeout(function () {
            if (document.activeElement !== activeEl) { close(); }
        }, 120);
    });

    document.addEventListener('keydown', function (e) {
        if (!activeEl || !box || box.style.display === 'none') { return; }
        if (e.target !== activeEl) { return; }

        if (e.key === 'ArrowDown') {
            e.preventDefault();
            highlight(activeIdx + 1 >= items.length ? 0 : activeIdx + 1);
        } else if (e.key === 'ArrowUp') {
            e.preventDefault();
            highlight(activeIdx - 1 < 0 ? items.length - 1 : activeIdx - 1);
        } else if (e.key === 'Enter') {
            if (activeIdx >= 0) { e.preventDefault(); choose(items[activeIdx]); }
        } else if (e.key === 'Escape' || e.key === 'Tab') {
            close();
        }
    });

    /* ---------- จำข้อความตอนกด Create Report ---------- */
    /*
     * ของเดิมจำทุกครั้งที่ออกจากช่อง (focusout) ทำให้คำที่พิมพ์ผิดหรือพิมพ์ค้างไว้ก็ถูกจำไปด้วย
     * ตอนนี้รวบรวมค่าทั้งหมดใส่ hidden input แล้วส่งไปพร้อมฟอร์ม ให้ฝั่ง server บันทึกทีเดียว
     */
    function collectMemo(form) {
        var claimed = [];
        var seen    = {};
        var out     = [];

        RULES.forEach(function (rule) {
            var nodes = form.querySelectorAll(rule.selector);
            for (var i = 0; i < nodes.length; i++) {
                var el = nodes[i];
                if (!isEditable(el) || claimed.indexOf(el) !== -1) { continue; }
                claimed.push(el);                       // ให้ rule ที่เจาะจงกว่าได้สิทธิ์ก่อน เหมือน resolve()
                var value = (el.value || '').trim();
                if (!value) { continue; }
                var key = rule.key(el);
                if (!key) { continue; }
                var dedupe = key + '\u0000' + value;
                if (seen[dedupe]) { continue; }
                seen[dedupe] = true;
                out.push({ key: key, value: value });
            }
        });

        return out;
    }

    document.addEventListener('submit', function (e) {
        var form = e.target;
        if (!form || form.nodeName !== 'FORM') { return; }

        var memo = collectMemo(form);
        if (!memo.length) { return; }

        var input = form.querySelector('input[name="autotext_memo"]');
        if (!input) {
            input = document.createElement('input');
            input.type = 'hidden';
            input.name = 'autotext_memo';
            form.appendChild(input);
        }
        input.value = JSON.stringify(memo);
    }, true);   // capture phase เพื่อให้ค่าถูกใส่ก่อน handler อื่นจะ submit จริง

    // dropdown เป็น position:fixed จึงต้องขยับตามเมื่อ scroll/resize (ของเดิมค้างอยู่กับที่)
    window.addEventListener('scroll', function () { if (activeEl) { position(); } }, true);
    window.addEventListener('resize', function () { if (activeEl) { position(); } });
    document.addEventListener('mousedown', function (e) {
        if (!activeEl) { return; }
        if (e.target === activeEl || (box && box.contains(e.target))) { return; }
        close();
    });
})();
