import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js";

const EXTENSION = "ethanfel.prompt_builder.profile_buttons";
const profileCache = new Map();

function widget(node, name) {
    return node.widgets?.find((w) => w.name === name);
}

function hideWidget(w) {
    if (!w) return;
    if (w.origType === undefined) w.origType = w.type;
    w.type = "hidden";
    w.hidden = true;
    w.computeSize = () => [0, -4];
}

function resizeNode(node) {
    const size = node.computeSize?.();
    if (size) node.setSize?.(size);
    app.graph?.setDirtyCanvas(true, true);
}

function nodeKey(nodeOrId) {
    return String(typeof nodeOrId === "object" ? nodeOrId?.id : nodeOrId);
}

function isProfileSaveNode(node) {
    return node?.comfyClass === "SxCPCharacterProfileSave" || node?.type === "SxCPCharacterProfileSave";
}

function isProfileLoadNode(node) {
    return node?.comfyClass === "SxCPCharacterProfileLoad" || node?.type === "SxCPCharacterProfileLoad";
}

function firstOutput(output, key) {
    const value = output?.[key];
    return Array.isArray(value) ? value[0] : value || "";
}

function cacheStatus(cache) {
    if (!cache?.profile_json) return "no cached profile";
    const name = cache.profile_name || "unnamed";
    const descriptor = cache.descriptor ? ` - ${cache.descriptor}` : "";
    const text = `cached: ${name}${descriptor}`;
    return text.length > 120 ? `${text.slice(0, 117)}...` : text;
}

function updateCacheWidget(node) {
    const cache = profileCache.get(nodeKey(node)) || node._sxcpProfileCache;
    if (node._sxcpCachedProfileWidget) node._sxcpCachedProfileWidget.value = cacheStatus(cache);
    node.setDirtyCanvas?.(true, true);
}

function setProfileCache(node, cache) {
    if (!node || !cache?.profile_json) return;
    node._sxcpProfileCache = cache;
    profileCache.set(nodeKey(node), cache);
    updateCacheWidget(node);
}

function getNodeById(id) {
    return app.graph?.getNodeById?.(Number(id)) || app.graph?._nodes_by_id?.[id] || app.graph?._nodes_by_id?.[Number(id)];
}

function addProfileChoice(profileName) {
    if (!profileName) return;
    for (const node of app.graph?._nodes || []) {
        if (!isProfileLoadNode(node)) continue;
        const profileWidget = widget(node, "profile_name");
        const values = profileWidget?.options?.values;
        if (Array.isArray(values) && !values.includes(profileName)) values.push(profileName);
    }
}

async function saveCachedProfile(node) {
    const cache = profileCache.get(nodeKey(node)) || node._sxcpProfileCache;
    if (!cache?.profile_json) {
        alert("No cached profile yet. Run the workflow once with this save node connected, then press Save Profile Now.");
        return;
    }
    const profileName = (widget(node, "profile_name")?.value || cache.profile_name || "").trim();
    if (!profileName) {
        alert("Fill profile_name before saving.");
        return;
    }

    try {
        const response = await api.fetchApi("/sxcp/profile/save_cached", {
            method: "POST",
            headers: {"Content-Type": "application/json"},
            body: JSON.stringify({
                profile_name: profileName,
                profile_json: cache.profile_json,
            }),
        });
        const data = await response.json();
        if (!response.ok) throw new Error(data?.error || response.statusText);
        setProfileCache(node, {
            profile_json: data.profile_json || cache.profile_json,
            descriptor: data.descriptor || cache.descriptor || "",
            profile_name: data.profile_name || profileName,
            saved_path: data.saved_path || "",
            status: data.status || "saved",
        });
        addProfileChoice(data.profile_name || profileName);
        alert(`Saved profile "${data.profile_name || profileName}".`);
    } catch (err) {
        console.error(`[${EXTENSION}] save cached profile failed`, err);
        alert(`Save failed: ${err}`);
    }
}

async function queueOnce(node, triggerName) {
    const trigger = widget(node, triggerName);
    if (!trigger) {
        alert(`Missing trigger widget: ${triggerName}`);
        return;
    }

    trigger.value = true;
    node.setDirtyCanvas?.(true, true);
    try {
        try {
            await app.queuePrompt(0, 1);
        } catch (_err) {
            await app.queuePrompt(0);
        }
    } catch (err) {
        console.error(`[${EXTENSION}] queue failed`, err);
        alert(`Queue failed: ${err}`);
    } finally {
        trigger.value = false;
        node.setDirtyCanvas?.(true, true);
    }
}

function setupSaveNode(node) {
    hideWidget(widget(node, "save_now"));
    if (!node._sxcpCachedProfileWidget) {
        node._sxcpCachedProfileWidget = node.addWidget("text", "cached_profile", "no cached profile", () => {});
        node._sxcpCachedProfileWidget.serialize = false;
    }
    if (!node._sxcpSaveButton) {
        node._sxcpSaveButton = node.addWidget("button", "Save Profile Now", null, () => saveCachedProfile(node));
    }
    updateCacheWidget(node);
    resizeNode(node);
}

function setupLoadNode(node) {
    hideWidget(widget(node, "delete_now"));
    hideWidget(widget(node, "rename_now"));
    if (!node._sxcpDeleteButton) {
        node._sxcpDeleteButton = node.addWidget("button", "Delete Selected Profile", null, () => {
            const profile = widget(node, "profile_name")?.value || "";
            if (!profile || profile === "manual") {
                alert("Select a saved profile before deleting.");
                return;
            }
            if (!confirm(`Delete saved profile "${profile}"?`)) return;
            queueOnce(node, "delete_now");
        });
    }
    if (!node._sxcpRenameButton) {
        node._sxcpRenameButton = node.addWidget("button", "Rename Selected Profile", null, () => {
            const profile = widget(node, "profile_name")?.value || "";
            const target = widget(node, "rename_to")?.value || "";
            if (!profile || profile === "manual") {
                alert("Select a saved profile before renaming.");
                return;
            }
            if (!target.trim()) {
                alert("Fill rename_to before renaming.");
                return;
            }
            if (!confirm(`Rename saved profile "${profile}" to "${target}"?`)) return;
            queueOnce(node, "rename_now");
        });
    }
    resizeNode(node);
}

app.registerExtension({
    name: EXTENSION,

    async setup() {
        api.addEventListener("executed", ({detail}) => {
            const node = getNodeById(detail?.node);
            if (!isProfileSaveNode(node)) return;
            const output = detail?.output || {};
            const profileJson = firstOutput(output, "profile_json");
            if (!profileJson) return;
            setProfileCache(node, {
                profile_json: profileJson,
                descriptor: firstOutput(output, "descriptor"),
                profile_name: firstOutput(output, "profile_name"),
                saved_path: firstOutput(output, "saved_path"),
                status: firstOutput(output, "status"),
            });
        });
    },

    async beforeRegisterNodeDef(nodeType, nodeData) {
        if (nodeData.name !== "SxCPCharacterProfileSave" && nodeData.name !== "SxCPCharacterProfileLoad") return;

        const onNodeCreated = nodeType.prototype.onNodeCreated;
        nodeType.prototype.onNodeCreated = function () {
            const result = onNodeCreated?.apply(this, arguments);
            if (nodeData.name === "SxCPCharacterProfileSave") setupSaveNode(this);
            if (nodeData.name === "SxCPCharacterProfileLoad") setupLoadNode(this);
            return result;
        };

        const onConfigure = nodeType.prototype.onConfigure;
        nodeType.prototype.onConfigure = function () {
            const result = onConfigure?.apply(this, arguments);
            queueMicrotask(() => {
                if (nodeData.name === "SxCPCharacterProfileSave") setupSaveNode(this);
                if (nodeData.name === "SxCPCharacterProfileLoad") setupLoadNode(this);
            });
            return result;
        };
    },
});
