// /AIWK_COMMON/modules/core/iframe_chat_agent.js
// AIWK EXT4 v126 - external common iframe chat/stream/settings base module
(function () {
    "use strict";
    const VERSION = "v127-nodrd-route-tab-switch-auth";
    const MODULE_ID = "core.iframe.chat_agent";
    const STORAGE_PREFIX = "AIWK_DOMAIN_MODULE_SETTINGS::";
    if (window.__AIWK_CORE_IFRAME_CHAT_AGENT_V118__) {
        aiwkModuleLog("already_loaded", { module_id: MODULE_ID });
        return;
    }
    window.__AIWK_CORE_IFRAME_CHAT_AGENT_V118__ = true;

    let collectObserver = null;
    let collectSeen = [];
    const collectSeenSet = new Set();

    function safeString(v) { return v === undefined || v === null ? "" : String(v); }
    function nowText() { try { return new Date().toLocaleTimeString(); } catch (err) { return ""; } }
    function frameInfo() {
        return { ok: 1, version: VERSION, module_id: MODULE_ID, is_top: window.top === window, href: location.href, origin: location.origin, hostname: location.hostname || "", title: document.title || "", referrer: document.referrer || "", ready_state: document.readyState || "" };
    }
    function sendEvent(action, event) {
        try {
            chrome.runtime.sendMessage({ type: "AIWK_IFRAME_AGENT_EVENT", action: action || "module_event", frame: frameInfo(), event: Object.assign({ module_id: MODULE_ID }, event || {}) }).catch(function () {});
        } catch (err) {}
    }
    function aiwkModuleLog(action, event) { sendEvent(action, event || {}); }
    function storageKey() { return STORAGE_PREFIX + (location.hostname || location.origin || "unknown") + "::" + MODULE_ID; }

    function queryDeep(root, selector, opt) {
        opt = opt || {};
        if (!root || !selector) return null;
        let direct = null;
        try { if (root.querySelector) direct = root.querySelector(selector); } catch (err) { err.code = "SELECTOR_INVALID"; throw err; }
        if (direct) return direct;
        if (opt.deep_shadow === false) return null;
        const list = root.querySelectorAll ? root.querySelectorAll("*") : [];
        for (const el of list) {
            if (el && el.shadowRoot) {
                const found = queryDeep(el.shadowRoot, selector, opt);
                if (found) return found;
            }
        }
        return null;
    }
    function queryAllDeep(root, selector, opt, out) {
        opt = opt || {};
        out = out || [];
        if (!root || !selector) return out;
        try { if (root.querySelectorAll) Array.prototype.forEach.call(root.querySelectorAll(selector), function (el) { out.push(el); }); } catch (err) { err.code = "SELECTOR_INVALID"; throw err; }
        if (opt.deep_shadow === false) return out;
        const list = root.querySelectorAll ? root.querySelectorAll("*") : [];
        Array.prototype.forEach.call(list, function (el) {
            if (el && el.shadowRoot) queryAllDeep(el.shadowRoot, selector, opt, out);
        });
        return out;
    }
    function setValue(el, value) {
        const v = safeString(value);
        if (!el) throw new Error("ELEMENT_MISSING");
        if (el.disabled || el.getAttribute("aria-disabled") === "true") throw new Error("INPUT_DISABLED");
        const tag = safeString(el.tagName).toLowerCase();
        const editable = el.isContentEditable || safeString(el.getAttribute && el.getAttribute("contenteditable")).toLowerCase() === "true" || tag === "div";
        try { el.focus && el.focus(); } catch (err) {}
        if (editable && !("value" in el)) {
            try {
                const sel = window.getSelection && window.getSelection();
                const range = document.createRange && document.createRange();
                if (sel && range) { range.selectNodeContents(el); range.deleteContents(); sel.removeAllRanges(); sel.addRange(range); }
            } catch (err) {}
            try { el.dispatchEvent(new InputEvent("beforeinput", { bubbles: true, cancelable: true, composed: true, inputType: "insertText", data: v })); } catch (err) {}
            try { document.execCommand && document.execCommand("insertText", false, v); } catch (err) { el.innerText = v; el.textContent = v; }
            if (!safeString(el.innerText || el.textContent).trim() && v) { el.innerText = v; el.textContent = v; }
        } else if (tag === "input" || tag === "textarea" || tag === "select" || "value" in el) {
            el.value = v;
        } else {
            el.textContent = v;
        }
        try { el.dispatchEvent(new InputEvent("input", { bubbles: true, cancelable: true, composed: true, inputType: "insertText", data: v })); } catch (err) { try { el.dispatchEvent(new Event("input", { bubbles: true, composed: true })); } catch (e) {} }
        try { el.dispatchEvent(new Event("change", { bubbles: true, composed: true })); } catch (err) {}
        try { el.focus && el.focus(); } catch (err) {}
    }
    function clickEl(el) {
        if (!el) throw new Error("ELEMENT_MISSING");
        if (el.disabled || el.getAttribute("aria-disabled") === "true") throw new Error("BUTTON_DISABLED");
        try { el.scrollIntoView && el.scrollIntoView({ block: "center", inline: "center" }); } catch (err) {}
        try { el.focus && el.focus(); } catch (err) {}
        try { el.dispatchEvent(new MouseEvent("mousedown", { bubbles: true, cancelable: true, view: window })); } catch (err) {}
        try { el.dispatchEvent(new MouseEvent("mouseup", { bubbles: true, cancelable: true, view: window })); } catch (err) {}
        el.click();
    }
    function pressEnter(el) {
        if (!el) throw new Error("ELEMENT_MISSING");
        try { el.focus && el.focus(); } catch (err) {}
        ["keydown", "keypress", "keyup"].forEach(function (type) {
            try { el.dispatchEvent(new KeyboardEvent(type, { key: "Enter", code: "Enter", keyCode: 13, which: 13, bubbles: true, cancelable: true })); } catch (err) {}
        });
    }
    function addProbe(text) {
        const msg = safeString(text || "AIWK module probe") + " / " + nowText();
        let probe = document.getElementById("aiwk_iframe_probe");
        if (!probe) {
            probe = document.createElement("button");
            probe.id = "aiwk_iframe_probe";
            probe.type = "button";
            probe.setAttribute("data-aiwk", "iframe-module-probe");
            probe.title = "AIWK probe 표시. 클릭하면 닫힙니다.";
            probe.style.cssText = "position:fixed;right:8px;bottom:8px;z-index:2147483646;background:#111827;color:#fff;font:11px/1.2 monospace;padding:4px 7px;border:1px solid #22c55e;border-radius:999px;box-shadow:0 2px 10px rgba(0,0,0,.25);max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:pointer";
            probe.addEventListener("click", function () { try { probe.remove(); } catch (err) {} });
            (document.body || document.documentElement).appendChild(probe);
        }
        probe.textContent = "AIWK probe OK";
        clearTimeout(probe.__aiwkTimer);
        probe.__aiwkTimer = setTimeout(function () { try { probe.remove(); } catch (err) {} }, 3000);
        sendEvent("module_probe_added", { probe_id: probe.id, text: msg, transient: 1 });
        return msg;
    }
    function appendLog(panel, text) {
        const log = panel && panel.querySelector('[data-aiwk-module-log="1"]');
        if (!log) return;
        const line = document.createElement("div");
        line.textContent = nowText() + " " + safeString(text).slice(0, 900);
        log.appendChild(line);
        while (log.children.length > 28) log.removeChild(log.firstChild);
        log.scrollTop = log.scrollHeight;
    }
    function getPanelValues(panel) {
        function val(sel) { const el = panel.querySelector(sel); return el ? safeString(el.value).trim() : ""; }
        function checked(sel) { const el = panel.querySelector(sel); return !!(el && el.checked); }
        return {
            input_selector: val('[data-aiwk-module-input-selector="1"]') || "#input",
            send_selector: val('[data-aiwk-module-send-selector="1"]') || "#send-button",
            collect_selector: val('[data-aiwk-module-collect-selector="1"]'),
            target: val('[data-aiwk-module-target="1"]'),
            message: (panel.querySelector('[data-aiwk-module-message="1"]') || {}).value || "",
            stream: checked('[data-aiwk-module-stream="1"]')
        };
    }
    function applySettingsToPanel(panel, settings) {
        settings = settings || {};
        function set(sel, v) { const el = panel.querySelector(sel); if (el && v !== undefined && v !== null) el.value = safeString(v); }
        function setChecked(sel, v) { const el = panel.querySelector(sel); if (el) el.checked = !!v; }
        set('[data-aiwk-module-input-selector="1"]', settings.input_selector);
        set('[data-aiwk-module-send-selector="1"]', settings.send_selector);
        set('[data-aiwk-module-collect-selector="1"]', settings.collect_selector);
        set('[data-aiwk-module-target="1"]', settings.target);
        setChecked('[data-aiwk-module-stream="1"]', settings.stream);
    }
    async function saveSettings(panel) {
        const v = getPanelValues(panel);
        const data = {};
        data[storageKey()] = {
            input_selector: v.input_selector,
            send_selector: v.send_selector,
            collect_selector: v.collect_selector,
            target: v.target,
            stream: v.stream,
            updated_at: Date.now(),
            version: VERSION
        };
        await chrome.storage.local.set(data);
        appendLog(panel, "OK 설정 저장 " + storageKey());
        sendEvent("module_settings_saved", data[storageKey()]);
    }
    async function loadSettings(panel) {
        const data = await chrome.storage.local.get([storageKey()]);
        const settings = data && data[storageKey()] ? data[storageKey()] : null;
        if (settings) {
            applySettingsToPanel(panel, settings);
            appendLog(panel, "OK 설정 로드 " + storageKey());
        } else {
            appendLog(panel, "settings empty / 기본값 사용");
        }
        return settings;
    }
    function runInput(panel, clickMode) {
        const v = getPanelValues(panel);
        const selector = v.input_selector;
        const sendSelector = v.send_selector;
        const value = v.message;
        if (!selector) { appendLog(panel, "ERR INPUT_SELECTOR_EMPTY / 해결: input selector 지정"); return; }
        if (!value && clickMode !== "click_only" && clickMode !== "enter_only") { appendLog(panel, "ERR MESSAGE_EMPTY / 해결: 보낼 메시지 입력"); return; }
        try {
            let input = null;
            if (clickMode !== "click_only") input = queryDeep(document, selector, { deep_shadow: true });
            if ((clickMode === "input_only" || clickMode === "input_click" || clickMode === "input_enter") && !input) {
                appendLog(panel, "ERR SELECTOR_NOT_FOUND " + selector + " / 해결: selector 확인 또는 도메인 preset 적용");
                sendEvent("module_input_error", { code: "SELECTOR_NOT_FOUND", selector: selector });
                return;
            }
            if (clickMode === "input_only" || clickMode === "input_click" || clickMode === "input_enter") {
                setValue(input, value);
                appendLog(panel, "OK input " + selector + " len=" + value.length);
            }
            if (clickMode === "enter_only" || clickMode === "input_enter") {
                const target = input || queryDeep(document, selector, { deep_shadow: true });
                if (!target) { appendLog(panel, "ERR ENTER_TARGET_NOT_FOUND " + selector); return; }
                pressEnter(target);
                appendLog(panel, "OK enter " + selector);
            }
            if (clickMode === "click_only" || clickMode === "input_click") {
                if (!sendSelector) { appendLog(panel, "ERR SEND_SELECTOR_EMPTY"); return; }
                const btn = queryDeep(document, sendSelector, { deep_shadow: true });
                if (!btn) { appendLog(panel, "ERR SEND_SELECTOR_NOT_FOUND " + sendSelector + " / 해결: send selector 확인"); return; }
                clickEl(btn);
                appendLog(panel, "OK click " + sendSelector);
            }
            sendEvent("module_chat_action", { mode: clickMode, selector: selector, send_selector: sendSelector, value_len: value.length });
        } catch (err) {
            appendLog(panel, "ERR " + (err && err.message ? err.message : String(err)));
            sendEvent("module_chat_error", { mode: clickMode, error: err && err.message ? err.message : String(err) });
        }
    }
    function textFromNode(node) {
        if (!node || node.nodeType !== 1) return "";
        let text = "";
        try { text = safeString(node.innerText || node.textContent || "").replace(/\s+/g, " ").trim(); } catch (err) { text = ""; }
        return text;
    }
    function rememberKey(key) {
        if (!key) return false;
        if (collectSeenSet.has(key)) return false;
        collectSeenSet.add(key);
        collectSeen.push(key);
        while (collectSeen.length > 300) {
            const old = collectSeen.shift();
            collectSeenSet.delete(old);
        }
        return true;
    }
    function streamMessage(panel, item) {
        const v = getPanelValues(panel);
        if (!v.stream) {
            appendLog(panel, "collect only " + safeString(item.text).slice(0, 80));
            return;
        }
        if (!v.target) {
            appendLog(panel, "ERR STREAM_TARGET_EMPTY / 해결: aiwk:// 대상 지정 후 수집 시작");
            return;
        }
        const payload = {
            type: "AIWK_IFRAME_STREAM_MESSAGE",
            target: v.target,
            text: item.text,
            html: item.html || "",
            collect_selector: v.collect_selector,
            frame: frameInfo(),
            module_id: MODULE_ID,
            at: new Date().toISOString(),
            ts: Date.now()
        };
        try {
            chrome.runtime.sendMessage(payload).then(function (res) {
                if (res && res.ok) appendLog(panel, "STREAM OK → " + v.target + " / " + item.text.slice(0, 80));
                else appendLog(panel, "ERR STREAM_SEND_FAIL " + (res && (res.code || res.error) || "unknown"));
            }).catch(function (err) { appendLog(panel, "ERR STREAM_SEND_FAIL " + (err && err.message ? err.message : String(err))); });
        } catch (err) {
            appendLog(panel, "ERR STREAM_SEND_EXCEPTION " + (err && err.message ? err.message : String(err)));
        }
    }
    function collectNode(panel, node, source) {
        const v = getPanelValues(panel);
        if (!v.collect_selector) return;
        const candidates = [];
        try {
            if (node && node.nodeType === 1 && node.matches && node.matches(v.collect_selector)) candidates.push(node);
            if (node && node.nodeType === 1 && node.querySelectorAll) Array.prototype.forEach.call(node.querySelectorAll(v.collect_selector), function (el) { candidates.push(el); });
        } catch (err) {
            appendLog(panel, "ERR COLLECT_SELECTOR_INVALID " + v.collect_selector);
            stopCollect(panel, "selector_invalid");
            return;
        }
        candidates.forEach(function (el) {
            const text = textFromNode(el);
            if (!text) return;
            const key = text.slice(0, 220);
            if (!rememberKey(key)) return;
            streamMessage(panel, { text: text, html: safeString(el.outerHTML || "").slice(0, 3000), source: source || "mutation" });
        });
    }
    function startCollect(panel) {
        const v = getPanelValues(panel);
        if (!v.collect_selector) { appendLog(panel, "ERR COLLECT_SELECTOR_EMPTY / 해결: 수집 selector 지정"); return; }
        if (v.stream && !v.target) { appendLog(panel, "ERR STREAM_TARGET_EMPTY / 해결: aiwk:// 대상 지정"); return; }
        if (collectObserver) { appendLog(panel, "collect already running"); return; }
        collectSeen = [];
        collectSeenSet.clear();
        try {
            const existing = queryAllDeep(document, v.collect_selector, { deep_shadow: true });
            appendLog(panel, "collect start selector=" + v.collect_selector + " existing=" + existing.length + " stream=" + (v.stream ? "1" : "0"));
            existing.slice(-20).forEach(function (el) { collectNode(panel, el, "initial"); });
        } catch (err) {
            appendLog(panel, "ERR COLLECT_SELECTOR_INVALID " + v.collect_selector + " / " + (err && err.message ? err.message : String(err)));
            return;
        }
        collectObserver = new MutationObserver(function (mutations) {
            mutations.forEach(function (m) {
                Array.prototype.forEach.call(m.addedNodes || [], function (node) { collectNode(panel, node, "mutation"); });
            });
        });
        try {
            collectObserver.observe(document.body || document.documentElement, { childList: true, subtree: true });
            appendLog(panel, "OK 수집 감시 시작");
            sendEvent("module_collect_start", { collect_selector: v.collect_selector, target: v.target, stream: v.stream ? 1 : 0 });
        } catch (err) {
            collectObserver = null;
            appendLog(panel, "ERR OBSERVER_START_FAIL " + (err && err.message ? err.message : String(err)));
        }
    }
    function stopCollect(panel, reason) {
        if (collectObserver) {
            try { collectObserver.disconnect(); } catch (err) {}
            collectObserver = null;
        }
        appendLog(panel, "collect stop " + safeString(reason || "manual"));
        sendEvent("module_collect_stop", { reason: reason || "manual" });
    }
    function makeFloatingWindow(panel, key, label) {
        const restoreId = key + "_restore";
        const title = panel.querySelector("[data-aiwk-win-title]") || panel.firstElementChild;
        panel.style.resize = "both";
        panel.style.overflow = "auto";
        const saved = (function () { try { return JSON.parse(localStorage.getItem(key) || "null"); } catch (err) { return null; } }());
        if (saved) {
            if (saved.left !== undefined) { panel.style.left = saved.left + "px"; panel.style.right = "auto"; }
            if (saved.top !== undefined) { panel.style.top = saved.top + "px"; panel.style.bottom = "auto"; }
            if (saved.width) panel.style.width = saved.width + "px";
            if (saved.height) panel.style.height = saved.height + "px";
        }
        function persist() {
            try {
                const r = panel.getBoundingClientRect();
                localStorage.setItem(key, JSON.stringify({ left: Math.round(r.left), top: Math.round(r.top), width: Math.round(r.width), height: Math.round(r.height) }));
            } catch (err) {}
        }
        if (title) {
            title.style.cursor = "move";
            title.addEventListener("mousedown", function (ev) {
                if (ev.button !== 0 || ev.target.closest("button,input,textarea,select,a,summary")) return;
                ev.preventDefault();
                const r = panel.getBoundingClientRect();
                const sx = ev.clientX, sy = ev.clientY, sl = r.left, st = r.top;
                panel.style.left = sl + "px"; panel.style.top = st + "px"; panel.style.right = "auto"; panel.style.bottom = "auto";
                function move(e) { panel.style.left = Math.max(0, sl + e.clientX - sx) + "px"; panel.style.top = Math.max(0, st + e.clientY - sy) + "px"; }
                function up() { document.removeEventListener("mousemove", move, true); document.removeEventListener("mouseup", up, true); persist(); }
                document.addEventListener("mousemove", move, true); document.addEventListener("mouseup", up, true);
            }, true);
        }
        let restore = document.getElementById(restoreId);
        if (!restore) {
            restore = document.createElement("button");
            restore.id = restoreId;
            restore.type = "button";
            restore.textContent = label || "AIWK chat";
            restore.style.cssText = "position:fixed;right:8px;bottom:8px;z-index:2147483647;background:#111827;color:#fff;border:1px solid #22c55e;border-radius:999px;padding:4px 8px;font:12px Arial;display:none;cursor:pointer";
            restore.addEventListener("click", function () { panel.style.display = "block"; restore.style.display = "none"; });
            (document.body || document.documentElement).appendChild(restore);
        }
        panel.__aiwkHideToButton = function () { panel.style.display = "none"; restore.style.display = "block"; persist(); };
        panel.__aiwkDestroy = function () { stopCollect(panel, "module_unload"); try { panel.remove(); } catch (err) {} try { restore.remove(); } catch (err) {} };
    }

    function renderDomainMenu(panel) {
        const box = panel && panel.querySelector ? panel.querySelector("[data-aiwk-domain-menu]") : null;
        if (!box) return;
        const items = (window.AIWK_DOMAIN_MENU_ITEMS || []).slice(0, 20);
        box.innerHTML = "";
        const head = document.createElement("div");
        head.style.cssText = "color:#bfdbfe;font-size:11px;margin-bottom:3px;";
        head.textContent = "도메인 메뉴 · " + (location.hostname || "-");
        box.appendChild(head);
        if (!items.length) {
            const empty = document.createElement("div");
            empty.style.cssText = "color:#9ca3af;font-size:11px;";
            empty.textContent = "등록된 도메인 메뉴 없음";
            box.appendChild(empty);
            return;
        }
        items.forEach(function (item, idx) {
            const btn = document.createElement("button");
            btn.type = "button";
            btn.textContent = item.label || item.id || ("menu" + idx);
            btn.title = item.title || item.description || "";
            btn.style.cssText = "background:#1f2937;color:#fff;border:1px solid #4b5563;border-radius:4px;padding:3px 6px;margin:2px;cursor:pointer;font:11px Arial;";
            btn.addEventListener("click", function () {
                try {
                    if (typeof item.run === "function") item.run(panel);
                    else if (item.event) window.dispatchEvent(new CustomEvent(item.event, { detail: { source:"iframe_domain_menu", item:item } }));
                    appendLog(panel, "OK domain menu " + (item.label || item.id || idx));
                } catch (err) { appendLog(panel, "ERR DOMAIN_MENU " + (err && err.message ? err.message : String(err))); }
            });
            box.appendChild(btn);
        });
    }
    function registerDomainMenuItems(items) {
        window.AIWK_DOMAIN_MENU_ITEMS = window.AIWK_DOMAIN_MENU_ITEMS || [];
        (Array.isArray(items) ? items : [items]).forEach(function (item) {
            if (!item) return;
            const id = String(item.id || item.label || Math.random()).trim();
            if (window.AIWK_DOMAIN_MENU_ITEMS.some(function (x) { return String(x.id || x.label) === id; })) return;
            window.AIWK_DOMAIN_MENU_ITEMS.push(Object.assign({}, item, { id:id }));
        });
        const panel = document.getElementById("aiwk_iframe_chat_agent_module_panel");
        if (panel) renderDomainMenu(panel);
    }
    function ensurePanel() {
        if (!document.documentElement) return null;
        let panel = document.getElementById("aiwk_iframe_chat_agent_module_panel");
        if (panel) return panel;
        panel = document.createElement("div");
        panel.id = "aiwk_iframe_chat_agent_module_panel";
        panel.setAttribute("data-aiwk-module-window", "1");
        panel.setAttribute("data-aiwk-window-label", "AIWK chat");
        panel.setAttribute("data-aiwk-module-id", MODULE_ID);
        panel.style.cssText = "position:fixed;right:8px;bottom:8px;z-index:2147483647;width:340px;min-width:240px;max-width:92vw;max-height:88vh;background:rgba(17,24,39,.97);color:#fff;font:12px/1.35 Arial,sans-serif;border:1px solid #22c55e;border-radius:8px;box-shadow:0 2px 14px rgba(0,0,0,.4);padding:7px;user-select:text";
        panel.innerHTML = [
            '<div data-aiwk-win-title="1" style="display:flex;gap:6px;align-items:center;margin-bottom:5px;"><button type="button" data-aiwk-domain-menu-toggle="1" title="도메인 메뉴" style="background:#111827;color:#fff;border:1px solid #4b5563;border-radius:4px;padding:1px 6px;cursor:pointer;">☰</button><b>AIWK iframe chat/stream</b><span style="margin-left:auto;color:#86efac;">v126</span><button type="button" data-aiwk-module-hide="1" style="background:#374151;color:#fff;border:0;border-radius:4px;padding:1px 5px;cursor:pointer;">×</button></div>',
            '<div data-aiwk-domain-menu="1" style="display:none;margin:0 0 5px 0;padding:5px;border:1px solid #374151;border-radius:5px;background:rgba(15,23,42,.95);"></div>',
            '<details style="margin-bottom:5px;"><summary style="cursor:pointer;color:#bfdbfe;">사용법</summary><div style="margin-top:4px;color:#d1d5db;font-size:11px;line-height:1.4;">1) 도메인 preset 또는 selector 직접 지정<br>2) target aiwk:// 대상 입력<br>3) 메시지 입력 후 입력+전송<br>4) 실시간 체크 후 수집시작<br>오류가 나면 로그의 해결 문구 기준으로 selector/target을 수정합니다.</div></details>',
            '<div style="word-break:break-all;color:#d1d5db;margin-bottom:5px;max-height:32px;overflow:hidden;">' + location.hostname + '</div>',
            '<label style="display:block;color:#facc15;font-size:11px;">input selector</label>',
            '<input data-aiwk-module-input-selector="1" value="#input" placeholder="input selector" style="box-sizing:border-box;width:100%;margin:0 0 4px 0;background:#030712;color:#fff;border:1px solid #4b5563;border-radius:4px;padding:4px;font:12px monospace;">',
            '<label style="display:block;color:#facc15;font-size:11px;">send selector</label>',
            '<input data-aiwk-module-send-selector="1" value="#send-button" placeholder="send selector" style="box-sizing:border-box;width:100%;margin:0 0 4px 0;background:#030712;color:#fff;border:1px solid #4b5563;border-radius:4px;padding:4px;font:12px monospace;">',
            '<label style="display:block;color:#facc15;font-size:11px;">collect selector</label>',
            '<input data-aiwk-module-collect-selector="1" value="" placeholder="새 메시지 수집 selector" style="box-sizing:border-box;width:100%;margin:0 0 4px 0;background:#030712;color:#fff;border:1px solid #4b5563;border-radius:4px;padding:4px;font:12px monospace;">',
            '<label style="display:block;color:#facc15;font-size:11px;">stream target aiwk://</label>',
            '<input data-aiwk-module-target="1" value="" placeholder="aiwk://ROOM@group.project.CLIENT" style="box-sizing:border-box;width:100%;margin:0 0 4px 0;background:#030712;color:#fff;border:1px solid #4b5563;border-radius:4px;padding:4px;font:12px monospace;">',
            '<textarea data-aiwk-module-message="1" placeholder="보낼 채팅 메시지" style="box-sizing:border-box;width:100%;height:48px;resize:vertical;margin:0 0 4px 0;background:#030712;color:#fff;border:1px solid #4b5563;border-radius:4px;padding:4px;font:12px/1.3 Arial,sans-serif;"></textarea>',
            '<div style="display:flex;gap:5px;flex-wrap:wrap;margin-bottom:4px;">',
            '<button type="button" data-aiwk-module-action="input_only" style="background:#7c3aed;color:#fff;border:0;border-radius:4px;padding:3px 6px;cursor:pointer;">입력</button>',
            '<button type="button" data-aiwk-module-action="input_enter" style="background:#2563eb;color:#fff;border:0;border-radius:4px;padding:3px 6px;cursor:pointer;">입력+Enter</button>',
            '<button type="button" data-aiwk-module-action="input_click" style="background:#dc2626;color:#fff;border:0;border-radius:4px;padding:3px 6px;cursor:pointer;">입력+전송</button>',
            '<button type="button" data-aiwk-module-action="click_only" style="background:#b45309;color:#fff;border:0;border-radius:4px;padding:3px 6px;cursor:pointer;">전송클릭</button>',
            '<button type="button" data-aiwk-module-probe="1" style="background:#059669;color:#fff;border:0;border-radius:4px;padding:3px 6px;cursor:pointer;">probe</button>',
            '</div>',
            '<div style="display:flex;gap:5px;flex-wrap:wrap;margin-bottom:4px;">',
            '<button type="button" data-aiwk-module-save="1" style="background:#7c3aed;color:#fff;border:0;border-radius:4px;padding:3px 6px;cursor:pointer;">설정저장</button>',
            '<button type="button" data-aiwk-module-load="1" style="background:#2563eb;color:#fff;border:0;border-radius:4px;padding:3px 6px;cursor:pointer;">설정로드</button>',
            '</div>',
            '<div style="display:flex;gap:5px;flex-wrap:wrap;margin-bottom:4px;align-items:center;">',
            '<label style="display:flex;gap:3px;align-items:center;color:#d1d5db;"><input type="checkbox" data-aiwk-module-stream="1"> 실시간 스트리밍</label>',
            '<button type="button" data-aiwk-module-collect-start="1" style="background:#059669;color:#fff;border:0;border-radius:4px;padding:3px 6px;cursor:pointer;">수집시작</button>',
            '<button type="button" data-aiwk-module-collect-stop="1" style="background:#6b7280;color:#fff;border:0;border-radius:4px;padding:3px 6px;cursor:pointer;">수집중지</button>',
            '</div>',
            '<div data-aiwk-module-log="1" style="max-height:120px;overflow:auto;background:rgba(0,0,0,.32);border:1px solid #374151;border-radius:4px;padding:3px;color:#d1d5db;font:11px/1.25 monospace;"></div>'
        ].join("");
        (document.body || document.documentElement).appendChild(panel);
        makeFloatingWindow(panel, "AIWK_WIN::iframe_chat_agent::" + (location.hostname || "unknown"), "AIWK chat");
        panel.addEventListener("click", function (ev) {
            const t = ev.target;
            if (!t) return;
            const action = t.getAttribute("data-aiwk-module-action");
            if (action) { runInput(panel, action); return; }
            if (t.getAttribute("data-aiwk-domain-menu-toggle")) { const m = panel.querySelector("[data-aiwk-domain-menu]"); if (m) { m.style.display = m.style.display === "none" ? "block" : "none"; renderDomainMenu(panel); } return; }
            if (t.getAttribute("data-aiwk-module-hide")) { if (panel.__aiwkHideToButton) panel.__aiwkHideToButton(); else panel.style.display = "none"; sendEvent("module_hide", {}); return; }
            if (t.getAttribute("data-aiwk-module-probe")) { appendLog(panel, addProbe("AIWK v126 iframe module probe")); return; }
            if (t.getAttribute("data-aiwk-module-save")) { saveSettings(panel).catch(function (err) { appendLog(panel, "ERR SAVE_FAIL " + (err && err.message ? err.message : String(err))); }); return; }
            if (t.getAttribute("data-aiwk-module-load")) { loadSettings(panel).catch(function (err) { appendLog(panel, "ERR LOAD_FAIL " + (err && err.message ? err.message : String(err))); }); return; }
            if (t.getAttribute("data-aiwk-module-collect-start")) { startCollect(panel); return; }
            if (t.getAttribute("data-aiwk-module-collect-stop")) { stopCollect(panel, "manual"); return; }
        });
        renderDomainMenu(panel);
        appendLog(panel, "module loaded " + VERSION);
        loadSettings(panel).catch(function (err) { appendLog(panel, "ERR AUTO_LOAD_FAIL " + (err && err.message ? err.message : String(err))); });
        sendEvent("module_panel_ready", { version: VERSION, panel_id: panel.id });
        return panel;
    }

    window.addEventListener("AIWK_DOMAIN_MENU_REGISTER", function (ev) {
        try { registerDomainMenuItems(ev && ev.detail ? (ev.detail.items || ev.detail) : []); } catch (err) {}
    });
    window.AIWKRegisterDomainMenu = registerDomainMenuItems;

    window.addEventListener("AIWK_IFRAME_CHAT_AGENT_APPLY_SETTINGS", function (ev) {
        try {
            const detail = ev && ev.detail ? ev.detail : {};
            const panel = ensurePanel();
            if (!panel) return;
            applySettingsToPanel(panel, detail.settings || detail || {});
            appendLog(panel, "OK domain preset applied" + (detail.label ? " / " + safeString(detail.label) : ""));
            sendEvent("module_domain_preset_applied", { label: detail.label || "", source: detail.source || "domain", keys: Object.keys(detail.settings || detail || {}) });
            if (detail.save === true) saveSettings(panel).catch(function (err) { appendLog(panel, "ERR DOMAIN_PRESET_SAVE_FAIL " + (err && err.message ? err.message : String(err))); });
        } catch (err) {
            sendEvent("module_domain_preset_error", { error: err && err.message ? err.message : String(err) });
        }
    });

    function destroy() {
        const panel = document.getElementById("aiwk_iframe_chat_agent_module_panel");
        if (panel && panel.__aiwkDestroy) panel.__aiwkDestroy();
        else if (panel) { stopCollect(panel, "module_unload"); panel.remove(); }
        const probe = document.getElementById("aiwk_iframe_probe");
        if (probe) try { probe.remove(); } catch (err) {}
        window.__AIWK_CORE_IFRAME_CHAT_AGENT_V118__ = false;
        sendEvent("module_destroy", { module_id: MODULE_ID });
    }
    window.addEventListener("AIWK_MODULE_UNLOAD", function (ev) {
        const id = ev && ev.detail && ev.detail.module_id;
        if (!id || id === MODULE_ID || id === "*") destroy();
    });
    function init() {
        if (!document.body && !document.documentElement) return;
        ensurePanel();
    }
    init();
    if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", init, { once: true });
}());
