// /AIWK_COMMON/core/aiwk_auth.js
// AIWK EXT4 v022
(function () {
    "use strict";

    const AIWK = window.AIWK;
    const roles = ["guest", "editor", "host", "dev"];

    const remoteTokenUrl = "https://i.yjm.kr/yw/conf/token.txt";

    function isHexToken(token) {
        return /^[a-f0-9]{64}$/i.test(String(token || "").trim());
    }

    function fetchRemoteToken() {
        return new Promise(function (resolve, reject) {
            if (typeof chrome === "undefined" || !chrome.runtime || typeof chrome.runtime.sendMessage !== "function") {
                reject(new Error("REMOTE_AUTH_BUS_UNAVAILABLE: chrome.runtime.sendMessage is missing. 해결: service_worker TAB BUS 사용 가능 상태를 확인하십시오."));
                return;
            }
            chrome.runtime.sendMessage({ type: "AIWK_AUTH_FETCH_REMOTE_TOKEN_BUS" }, function (res) {
                const lastError = chrome.runtime.lastError;
                if (lastError) {
                    reject(new Error("REMOTE_AUTH_BUS_MESSAGE_FAIL: " + lastError.message));
                    return;
                }
                if (!res || !res.ok) {
                    reject(new Error((res && (res.code + ": " + res.error + " / 해결: " + (res.fix || "service_worker TAB TAB BUS와 host_permissions/tabs/scripting를 확인하십시오."))) || "REMOTE_TOKEN_TABBUS_READ_FAIL"));
                    return;
                }
                resolve(String(res.token || "").trim());
            });
        });
    }

    async function loginRemote(inputToken) {
        const token = String(inputToken || "").trim();
        if (!token) {
            throw new Error("TOKEN_EMPTY: token을 입력하십시오.");
        }
        if (!isHexToken(token)) {
            throw new Error("TOKEN_FORMAT_INVALID: 서버 토큰은 64자리 hex 문자열이어야 합니다.");
        }
        const serverToken = await fetchRemoteToken();
        if (!serverToken) {
            throw new Error("REMOTE_TOKEN_EMPTY: 서버 token.txt가 비어 있습니다.");
        }
        if (serverToken !== token) {
            throw new Error("TOKEN_MISMATCH: 입력 token과 서버 token.txt 값이 다릅니다.");
        }
        const parsed = buildResult(true, "dev", token, {
            role: "dev",
            perm: "ADMIN",
            source: "remote-token",
            url: remoteTokenUrl
        }, "remote-token", "remote token verified as dev");
        applyParsed(parsed, true);
        return parsed;
    }

    const permRoleMap = {
        "USER": "editor",
        "HOST": "host",
        "ADMIN": "dev",
        "guest": "guest",
        "editor": "editor",
        "dev": "dev",
        "host": "host"
    };

    function base64UrlDecode(s) {
        const safe = String(s || "").replace(/-/g, "+").replace(/_/g, "/");
        const pad = safe.length % 4 ? "=".repeat(4 - (safe.length % 4)) : "";
        return atob(safe + pad);
    }

    function normalizeRole(value) {
        const raw = String(value || "").trim();
        if (!raw) {
            return "guest";
        }
        if (roles.indexOf(raw) >= 0) {
            return raw;
        }
        const upper = raw.toUpperCase();
        return permRoleMap[upper] || null;
    }

    function userTypeFromPayload(payload, role) {
        payload = payload || {};
        const raw = String(payload.user_type || payload.userType || payload.perm || payload.role || role || "").trim();
        const up = raw.toUpperCase();
        if (up === "ADMIN" || up === "DEV") return "AIWK1_ADMIN";
        if (up === "USER" || up === "EDITOR") return "AIWK1_USER";
        if (up === "HOST") return "AIWK1_HOST";
        if (role === "dev") return "AIWK1_ADMIN";
        if (role === "editor") return "AIWK1_USER";
        return "GUEST";
    }

    function buildResult(ok, role, token, payload, mode, message, code) {
        payload = payload || {};
        role = role || "guest";
        const userType = userTypeFromPayload(payload, role);
        payload.user_type = payload.user_type || userType;
        return {
            ok: !!ok,
            role: role,
            user_type: userType,
            token: token || "",
            payload: payload,
            mode: mode || "empty",
            message: message || "",
            code: code || ""
        };
    }

    function parseToken(raw) {
        const token = String(raw || "").trim();
        let role = null;
        let payload = null;
        let decoded = "";

        if (!token) {
            return buildResult(true, "guest", "", {}, "empty", "guest");
        }

        role = normalizeRole(token);
        if (role) {
            return buildResult(true, role, token, { role: role }, "role", "role=" + role);
        }

        if (token.indexOf("AIWK1.") === 0) {
            const parts = token.split(".");
            if (parts.length !== 3) {
                return buildResult(false, "guest", token, {}, "AIWK1", "AIWK1 token must have 3 parts.", "TOKEN_PARTS_INVALID");
            }
            try {
                decoded = base64UrlDecode(parts[1]);
                payload = JSON.parse(decoded);
            } catch (err) {
                return buildResult(false, "guest", token, {}, "AIWK1", "AIWK1 payload decode failed: " + err.message, "TOKEN_PAYLOAD_INVALID");
            }
            role = normalizeRole(payload.role || payload.perm || "");
            if (!role) {
                return buildResult(false, "guest", token, payload, "AIWK1", "AIWK1 payload has no valid role/perm.", "TOKEN_ROLE_INVALID");
            }
            return buildResult(true, role, token, payload, "AIWK1", "AIWK1 role=" + role);
        }

        try {
            payload = JSON.parse(token);
            role = normalizeRole(payload.role || payload.perm || "");
            if (!role) {
                return buildResult(false, "guest", token, payload, "json", "JSON token has no valid role/perm.", "TOKEN_ROLE_INVALID");
            }
            return buildResult(true, role, token, payload, "json", "JSON role=" + role);
        } catch (errJson) {
            try {
                decoded = atob(token);
                payload = JSON.parse(decoded);
                role = normalizeRole(payload.role || payload.perm || "");
                if (!role) {
                    return buildResult(false, "guest", token, payload, "base64-json", "Base64 token has no valid role/perm.", "TOKEN_ROLE_INVALID");
                }
                return buildResult(true, role, token, payload, "base64-json", "Base64 role=" + role);
            } catch (errB64) {
                return buildResult(false, "guest", token, {}, "unknown", "Token parse failed. Use guest/editor/host/dev, JSON, base64 JSON, or AIWK1 token.", "TOKEN_PARSE_FAIL");
            }
        }
    }

    function applyParsed(parsed, save) {
        if (!parsed || !parsed.ok) {
            return false;
        }
        AIWK.auth.token = parsed.token || "";
        AIWK.auth.role = parsed.role || "guest";
        AIWK.auth.payload = parsed.payload || {};
        AIWK.auth.user_type = parsed.user_type || userTypeFromPayload(AIWK.auth.payload, AIWK.auth.role);
        AIWK.auth.mode = parsed.mode || "empty";
        if (save) {
            AIWK.store.set("auth_token", AIWK.auth.token);
            AIWK.store.set("auth_payload", AIWK.auth.payload);
            AIWK.store.set("auth_role", AIWK.auth.role);
            AIWK.store.set("auth_mode", AIWK.auth.mode);
            AIWK.store.set("auth_user_type", AIWK.auth.user_type);
        }
        AIWK.state.set("role", AIWK.auth.role);
        AIWK.events.emit("auth:change", {
            role: AIWK.auth.role,
            user_type: AIWK.auth.user_type,
            token: AIWK.auth.token,
            payload: AIWK.auth.payload,
            mode: AIWK.auth.mode
        });
        return true;
    }

    AIWK.auth = {
        roles: roles.slice(),
        role: "guest",
        token: "",
        payload: {},
        user_type: "GUEST",
        mode: "empty",
        remoteTokenUrl: remoteTokenUrl,
        isHexToken: isHexToken,
        fetchRemoteToken: fetchRemoteToken,
        loginRemote: loginRemote,
        parseToken: parseToken,
        normalizeRole: normalizeRole,
        init: function () {
            const token = AIWK.store.get("auth_token", "");
            const savedMode = AIWK.store.get("auth_mode", "");
            const savedRole = AIWK.store.get("auth_role", "");
            const savedPayload = AIWK.store.get("auth_payload", {});
            const savedUserType = AIWK.store.get("auth_user_type", "");
            if (savedUserType && savedPayload && !savedPayload.user_type) savedPayload.user_type = savedUserType;
            if (token && isHexToken(token) && savedMode === "remote-token" && savedRole === "dev") {
                applyParsed(buildResult(true, "dev", token, savedPayload || { role: "dev", source: "remote-token" }, "remote-token", "remote token restored"), false);
                return AIWK.auth.role;
            }
            const parsed = parseToken(token);
            if (!parsed.ok) {
                AIWK.store.remove("auth_token");
                AIWK.store.remove("auth_payload");
                AIWK.store.remove("auth_role");
                AIWK.store.remove("auth_mode");
                AIWK.store.remove("auth_user_type");
                console.error("AIWK auth init failed", parsed.message);
                applyParsed(buildResult(true, "guest", "", {}, "empty", "fallback guest"), false);
                return "guest";
            }
            applyParsed(parsed, false);
            return AIWK.auth.role;
        },
        setToken: function (token) {
            const parsed = parseToken(token);
            if (!parsed.ok) {
                throw new Error(parsed.code + ": " + parsed.message);
            }
            applyParsed(parsed, true);
            return parsed;
        },
        applyVerifiedRemoteToken: function (token, payload) {
            token = String(token || "").trim();
            if (!isHexToken(token)) {
                throw new Error("TOKEN_FORMAT_INVALID: 서버 토큰은 64자리 hex 문자열이어야 합니다.");
            }
            const parsed = buildResult(true, "dev", token, Object.assign({
                role: "dev",
                perm: "ADMIN",
                source: "popup-verified-remote-token",
                url: remoteTokenUrl
            }, payload || {}), "remote-token", "remote token verified by popup");
            applyParsed(parsed, true);
            return parsed;
        },
        applyPopupLogin: function (profile) {
            profile = profile || {};
            const token = String(profile.authToken || profile.token || "").trim();
            const mode = String(profile.authMode || profile.mode || "").trim();
            if (!token) {
                AIWK.auth.clearToken();
                return buildResult(true, "guest", "", { user_type:"GUEST", role:"guest" }, "empty", "guest popup login");
            }
            if (mode === "remote-token") {
                return AIWK.auth.applyVerifiedRemoteToken(token, profile.authPayload || profile.payload || {});
            }
            return AIWK.auth.setToken(token);
        },
        clearToken: function () {
            AIWK.store.remove("auth_token");
            AIWK.store.remove("auth_payload");
            AIWK.store.remove("auth_role");
            AIWK.store.remove("auth_mode");
                AIWK.store.remove("auth_user_type");
            const parsed = buildResult(true, "guest", "", {}, "empty", "cleared");
            applyParsed(parsed, false);
            return parsed;
        },
        getRole: function () {
            return AIWK.auth.role || "guest";
        },
        getPayload: function () {
            return AIWK.auth.payload || {};
        },
        getUserType: function () {
            return AIWK.auth.user_type || userTypeFromPayload(AIWK.auth.payload || {}, AIWK.auth.role || "guest");
        },
        can: function (needRole) {
            const need = normalizeRole(needRole || "guest") || "guest";
            const current = AIWK.auth.role || "guest";
            if (need === "guest") return true;
            if (current === "dev") return true;
            if (current === "editor") return need === "editor";
            if (current === "host") return need === "host";
            return false;
        }
    };
}());
