// /AIWK_COMMON/modules/dev/manual_test_manager.js
// AIWK EXT4 v036
(function () {
    "use strict";

    const AIWK = window.AIWK;
    const STORE_KEY = "manual_tests_v1";
    const HIDE_DONE_KEY = "manual_tests_hide_done_v1";
    const SHOW_TRASH_KEY = "manual_tests_show_trash_v1";
    const REWARD_KEY = "manual_test_reward_v036";

    function el(tag, className, text) {
        const node = document.createElement(tag);
        if (className) {
            node.className = className;
        }
        if (text !== undefined) {
            node.textContent = text;
        }
        return node;
    }

    function safeJson(obj) {
        return JSON.stringify(obj, null, 2);
    }

    function nowIso() {
        return new Date().toISOString();
    }

    function clone(obj) {
        return JSON.parse(JSON.stringify(obj || {}));
    }

    function defaultRectFor(winId) {
        const map = {
            "test-launcher-win": { x: 40, y: 70, w: 700, h: 620 },
            "manual-test-manager-win": { x: 70, y: 90, w: 760, h: 650 },
            "hello-win": { x: 590, y: 70, w: 440, h: 360 },
            "auth-win": { x: 40, y: 70, w: 520, h: 560 },
            "registry-win": { x: 40, y: 520, w: 620, h: 430 },
            "bus-test-win": { x: 40, y: 90, w: 560, h: 520 },
            "wss-test-win": { x: 620, y: 90, w: 580, h: 560 },
            "nr-test-win": { x: 1220, y: 90, w: 580, h: 520 }
        };
        return map[winId] || { x: 120, y: 120, w: 560, h: 420 };
    }

    function statusMeta(item) {
        if (item.deleted) {
            return { icon: "🗑", text: "휴지통", cls: "aiwk-test-row-trash" };
        }
        if (item.status === "done") {
            return { icon: "✅", text: "완료", cls: "aiwk-test-row-done" };
        }
        if (item.status === "bug") {
            return { icon: "🐞", text: "오류", cls: "aiwk-test-row-bug" };
        }
        if (item.status === "module") {
            return { icon: "📦", text: "모듈", cls: "aiwk-test-row-module" };
        }
        return { icon: "🧪", text: "테스트", cls: "aiwk-test-row-test" };
    }

    function makeId(title) {
        const base = String(title || "manual-test").trim().toLowerCase().replace(/[^a-z0-9가-힣]+/g, "-").replace(/^-+|-+$/g, "");
        return (base || "manual-test") + "-" + Date.now().toString(36);
    }

    function mergeItems(base, saved) {
        const out = [];
        const byId = {};
        (base || []).forEach(function (item) {
            const row = clone(item);
            byId[row.id] = row;
            out.push(row);
        });
        (saved || []).forEach(function (item) {
            if (!item || !item.id) {
                return;
            }
            if (byId[item.id]) {
                Object.assign(byId[item.id], item);
                return;
            }
            out.push(clone(item));
        });
        return out;
    }

    function createWinOnce(cfg) {
        const old = AIWK.win.get(cfg.id);
        if (old) {
            old.bringFront();
            return old;
        }
        const win = AIWK.win.create(cfg);
        try { AIWK.group.addWin("zone-main", cfg.id); } catch (err) {}
        return win;
    }

    function saveItems(items) {
        AIWK.store.set(STORE_KEY, items || []);
    }

    async function loadItems() {
        const data = await AIWK.jsonLoader.load(AIWK.paths.manualTestsJson);
        const saved = AIWK.store.get(STORE_KEY, []);
        return mergeItems(data.items || [], saved || []);
    }

    async function executeItem(item) {
        const action = item.action || "open_win";
        const winId = item.win_id || (item.id + "-win");
        const moduleName = item.module || "";
        let win = AIWK.win.get(winId);
        if (action === "open_win" || action === "run_win" || action === "selftest_win") {
            if (!win) {
                if (!moduleName) {
                    throw new Error("MODULE_EMPTY: module 값이 없습니다. 해결: 테스트 항목에 module을 입력하십시오.");
                }
                win = createWinOnce({
                    id: winId,
                    title: (item.title || winId) + " " + (AIWK.version || ""),
                    module: moduleName,
                    rect: defaultRectFor(winId)
                });
            } else {
                win.bringFront();
            }
        }
        if (action === "open_win") {
            return { ok: 1, action: action, win_id: winId, module: moduleName, message: "window opened" };
        }
        if (action === "run_win") {
            const res = await AIWK.win.run(winId, { source: "dev.manual_test_manager", test_id: item.id, at: nowIso() });
            return { ok: 1, action: action, win_id: winId, module: moduleName, result: res };
        }
        if (action === "selftest_win") {
            const res = await win.test();
            return { ok: res && res.ok ? 1 : 0, action: action, win_id: winId, module: moduleName, result: res };
        }
        if (action === "copy_expected") {
            await navigator.clipboard.writeText(String(item.expected || ""));
            return { ok: 1, action: action, copied: "expected" };
        }
        throw new Error("UNKNOWN_TEST_ACTION: " + action + ". 해결: open_win/run_win/selftest_win/copy_expected 중 하나를 사용하십시오.");
    }

    AIWK.modules.register("dev.manual_test_manager", {
        name: "dev.manual_test_manager",
        run: async function (ctx) {
            return {
                ok: 1,
                version: AIWK.version || "",
                module: "dev.manual_test_manager",
                win_id: ctx.win.id,
                count: AIWK.store.get(STORE_KEY, []).length,
                message: "manual test manager ready"
            };
        },
        mount: function (ctx) {
            const box = el("div", "aiwk-manual-test-manager select-text");
            const title = el("div", "text-base font-semibold select-text", "수동 테스트 관리 " + (AIWK.version || ""));
            const desc = el("div", "text-xs select-text", "JSON 정의 항목을 사람이 직접 수행하고, 완료/오류/휴지통 상태를 저장합니다. 휴지통은 실제 삭제가 아니라 숨김 토글입니다.");

            const toolbar = el("div", "aiwk-manual-test-toolbar select-text");
            const hideDoneLabel = el("label", "aiwk-manual-test-toggle select-text", "");
            const hideDone = document.createElement("input");
            hideDone.type = "checkbox";
            hideDone.checked = !!AIWK.store.get(HIDE_DONE_KEY, false);
            hideDoneLabel.appendChild(hideDone);
            hideDoneLabel.appendChild(document.createTextNode(" 완료숨김"));
            hideDoneLabel.title = "완료 체크된 항목을 화면에서 숨깁니다.";

            const showTrashLabel = el("label", "aiwk-manual-test-toggle select-text", "");
            const showTrash = document.createElement("input");
            showTrash.type = "checkbox";
            showTrash.checked = !!AIWK.store.get(SHOW_TRASH_KEY, false);
            showTrashLabel.appendChild(showTrash);
            showTrashLabel.appendChild(document.createTextNode(" 🗑 휴지통 보기"));
            showTrashLabel.title = "휴지통 체크된 항목을 다시 표시합니다.";

            const addBtn = el("button", "aiwk-btn", "+ 항목추가");
            const saveBtn = el("button", "aiwk-btn", "저장");
            const reloadBtn = el("button", "aiwk-btn", "JSON 다시읽기");
            toolbar.appendChild(hideDoneLabel);
            toolbar.appendChild(showTrashLabel);
            toolbar.appendChild(addBtn);
            toolbar.appendChild(saveBtn);
            toolbar.appendChild(reloadBtn);

            const form = el("div", "aiwk-manual-test-form select-text");
            form.innerHTML = "" +
                "<input data-field=\"title\" placeholder=\"테스트 제목\">" +
                "<input data-field=\"module\" placeholder=\"module 예: sample.hello\">" +
                "<input data-field=\"win_id\" placeholder=\"win_id 예: hello-win\">" +
                "<select data-field=\"action\"><option value=\"open_win\">open_win</option><option value=\"run_win\">run_win</option><option value=\"selftest_win\">selftest_win</option><option value=\"copy_expected\">copy_expected</option></select>" +
                "<textarea data-field=\"expected\" class=\"aiwk-textarea\" placeholder=\"예상 답/성공 조건\"></textarea>" +
                "<textarea data-field=\"manual_steps\" class=\"aiwk-textarea\" placeholder=\"수동 실행 방법\"></textarea>";

            const list = el("div", "aiwk-manual-test-list select-text", "");
            const resultBox = el("pre", "aiwk-run-result select-text", "수동 테스트 항목을 읽는 중입니다.");
            let items = [];

            function persistToggles() {
                AIWK.store.set(HIDE_DONE_KEY, !!hideDone.checked);
                AIWK.store.set(SHOW_TRASH_KEY, !!showTrash.checked);
            }

            function setResult(obj) {
                resultBox.textContent = typeof obj === "string" ? obj : safeJson(obj);
                resultBox.scrollTop = resultBox.scrollHeight;
            }

            function summary() {
                const total = items.filter(function (x) { return !x.deleted; }).length;
                const done = items.filter(function (x) { return !x.deleted && x.status === "done"; }).length;
                const bug = items.filter(function (x) { return !x.deleted && x.status === "bug"; }).length;
                const trash = items.filter(function (x) { return x.deleted; }).length;
                return "전체:" + total + " / 완료:" + done + " / 오류:" + bug + " / 휴지통:" + trash;
            }

            function render() {
                persistToggles();
                list.innerHTML = "";
                items.forEach(function (item) {
                    if (hideDone.checked && !item.deleted && item.status === "done") {
                        return;
                    }
                    if (item.deleted && !showTrash.checked) {
                        return;
                    }
                    const meta = statusMeta(item);
                    const row = el("div", "aiwk-manual-test-row " + meta.cls + " select-text", "");
                    row.dataset.testId = item.id;

                    const head = el("div", "aiwk-manual-test-head select-text", "");
                    const icon = el("span", "aiwk-manual-test-icon", meta.icon);
                    icon.title = meta.text;
                    const name = el("div", "aiwk-manual-test-name select-text", item.title || item.id);
                    const group = el("span", "aiwk-manual-test-group select-text", item.group || "manual");
                    head.appendChild(icon);
                    head.appendChild(name);
                    head.appendChild(group);

                    const actions = el("div", "aiwk-manual-test-actions select-text", "");
                    const runBtn = el("button", "aiwk-btn", "수행");
                    runBtn.dataset.rowAction = "run";
                    const doneLabel = el("label", "aiwk-manual-test-toggle select-text", "");
                    const doneChk = document.createElement("input");
                    doneChk.type = "checkbox";
                    doneChk.checked = item.status === "done";
                    doneChk.dataset.rowAction = "done";
                    doneLabel.appendChild(doneChk);
                    doneLabel.appendChild(document.createTextNode(" 완료"));
                    const bugLabel = el("label", "aiwk-manual-test-toggle select-text", "");
                    const bugChk = document.createElement("input");
                    bugChk.type = "checkbox";
                    bugChk.checked = item.status === "bug";
                    bugChk.dataset.rowAction = "bug";
                    bugLabel.appendChild(bugChk);
                    bugLabel.appendChild(document.createTextNode(" 오류"));
                    const trashLabel = el("label", "aiwk-manual-test-toggle select-text", "");
                    const trashChk = document.createElement("input");
                    trashChk.type = "checkbox";
                    trashChk.checked = !!item.deleted;
                    trashChk.dataset.rowAction = "trash";
                    trashLabel.appendChild(trashChk);
                    trashLabel.appendChild(document.createTextNode(" 🗑"));
                    trashLabel.title = "실제 삭제하지 않고 휴지통 상태만 토글합니다.";
                    const copyBtn = el("button", "aiwk-btn", "조건복사");
                    copyBtn.dataset.rowAction = "copy";
                    actions.appendChild(runBtn);
                    actions.appendChild(doneLabel);
                    actions.appendChild(bugLabel);
                    actions.appendChild(trashLabel);
                    actions.appendChild(copyBtn);

                    const info = el("div", "aiwk-manual-test-info select-text", "");
                    info.innerHTML = "<b>module</b>: " + String(item.module || "") + " / <b>win</b>: " + String(item.win_id || "") + " / <b>action</b>: " + String(item.action || "open_win") + " / <b>오류횟수</b>: " + String(item.error_count || 0) + " / <b>소요</b>: " + String(item.duration_sec || 0) + "초<br><b>예상 답</b>: " + String(item.expected || "") + "<br><b>수동 조건</b>: " + String(item.manual_steps || "");

                    row.appendChild(head);
                    row.appendChild(actions);
                    row.appendChild(info);
                    list.appendChild(row);
                });
                ctx.status.textContent = summary();
                saveItems(items);
            }

            function getItem(id) {
                return items.find(function (x) { return x.id === id; });
            }

            addBtn.addEventListener("click", function () {
                const data = {};
                form.querySelectorAll("[data-field]").forEach(function (node) {
                    data[node.dataset.field] = String(node.value || "").trim();
                });
                if (!data.title) {
                    setResult("ERROR: 테스트 제목을 입력하십시오.");
                    return;
                }
                const item = {
                    id: makeId(data.title),
                    group: "manual",
                    title: data.title,
                    module: data.module,
                    win_id: data.win_id,
                    action: data.action || "open_win",
                    expected: data.expected,
                    manual_steps: data.manual_steps,
                    status: "test",
                    created_at: nowIso()
                };
                items.push(item);
                saveItems(items);
                render();
                setResult({ ok: 1, action: "add", item: item });
            });

            saveBtn.addEventListener("click", function () {
                saveItems(items);
                setResult({ ok: 1, action: "save", count: items.length, summary: summary() });
                render();
            });

            reloadBtn.addEventListener("click", function () {
                loadItems().then(function (loaded) {
                    items = loaded;
                    render();
                    setResult({ ok: 1, action: "reload", count: items.length, summary: summary() });
                }).catch(function (err) {
                    setResult("ERROR: " + err.message);
                });
            });

            hideDone.addEventListener("change", render);
            showTrash.addEventListener("change", render);

            list.addEventListener("click", function (ev) {
                const row = ev.target.closest("[data-test-id]");
                if (!row) {
                    return;
                }
                const item = getItem(row.dataset.testId);
                if (!item) {
                    return;
                }
                const actionNode = ev.target.closest("[data-row-action]");
                if (!actionNode) {
                    return;
                }
                const action = actionNode.dataset.rowAction;
                if (action === "run") {
                    executeItem(item).then(function (res) {
                        item.last_result = res;
                        item.last_run_at = nowIso();
                        item.error_count = item.error_count || 0;
                        if (!res || !res.ok) {
                            item.status = "bug";
                            item.error_count += 1;
                        }
                        saveItems(items);
                        render();
                        setResult(res);
                    }).catch(function (err) {
                        item.status = "bug";
                        item.error_count = (item.error_count || 0) + 1;
                        item.last_error = err.message;
                        item.last_run_at = nowIso();
                        saveItems(items);
                        render();
                        setResult({ ok: 0, test_id: item.id, error: err.message, error_count: item.error_count });
                    });
                    return;
                }
                if (action === "done") {
                    item.status = actionNode.checked ? "done" : "test";
                    item.done_at = actionNode.checked ? nowIso() : "";
                    if (actionNode.checked && item.last_run_at) {
                        const sec = Math.max(0, Math.round((Date.parse(item.done_at) - Date.parse(item.last_run_at)) / 1000));
                        item.duration_sec = isNaN(sec) ? 0 : sec;
                    }
                    if (actionNode.checked) {
                        const doneCount = items.filter(function (x) { return !x.deleted && x.status === "done"; }).length;
                        AIWK.store.set(REWARD_KEY, { level: doneCount, last_done_id: item.id, last_done_at: item.done_at });
                        setResult({ ok: 1, reward: "축하드립니다. " + doneCount + "랩에 도달하셨습니다.", test_id: item.id, done_at: item.done_at, duration_sec: item.duration_sec || 0 });
                    }
                    saveItems(items);
                    render();
                    return;
                }
                if (action === "bug") {
                    item.status = actionNode.checked ? "bug" : "test";
                    saveItems(items);
                    render();
                    return;
                }
                if (action === "trash") {
                    item.deleted = !!actionNode.checked;
                    item.deleted_at = actionNode.checked ? nowIso() : "";
                    saveItems(items);
                    render();
                    return;
                }
                if (action === "copy") {
                    navigator.clipboard.writeText(String(item.expected || "")).then(function () {
                        setResult({ ok: 1, action: "copy_expected", test_id: item.id });
                    }).catch(function (err) {
                        setResult({ ok: 0, action: "copy_expected", error: err.message });
                    });
                }
            });

            box.appendChild(title);
            box.appendChild(desc);
            box.appendChild(toolbar);
            box.appendChild(form);
            box.appendChild(list);
            box.appendChild(resultBox);
            ctx.body.appendChild(box);
            if (AIWK.ui && typeof AIWK.ui.applyPlaceholderTooltips === "function") {
                AIWK.ui.applyPlaceholderTooltips(box);
            }

            loadItems().then(function (loaded) {
                items = loaded;
                render();
                setResult({ ok: 1, action: "load", count: items.length, summary: summary(), version: AIWK.version || "" });
            }).catch(function (err) {
                setResult("ERROR: " + err.message);
                ctx.status.textContent = "manual test load stopped";
            });
        }
    });
}());
