import { app } from "../../../scripts/app.js";
import { addMenuHandler } from "./common/utils.js";
import { findWidgetByName } from "./common/utils.js";

function replaceNode(oldNode, newNodeName) {
    const newNode = LiteGraph.createNode(newNodeName);
    if (!newNode) {
        return;
    }
    app.graph.add(newNode);

    newNode.pos = oldNode.pos.slice();
    newNode.size = oldNode.size.slice();

    // Transfer widget values
    const widgetMapping = {
        "ckpt_name": "base_ckpt_name",
        "vae_name": "vae_name",
        "clip_skip": "base_clip_skip",
        "positive": "positive",
        "negative": "negative",
        "prompt_style": "prompt_style",
        "empty_latent_width": "empty_latent_width",
        "empty_latent_height": "empty_latent_height",
        "batch_size": "batch_size"
    };

    let effectiveWidgetMapping = widgetMapping;

    // Invert the mapping when going from "Eff. Loader SDXL" to "Efficient Loader"
    if (oldNode.type === "Eff. Loader SDXL" && newNodeName === "Efficient Loader") {
        effectiveWidgetMapping = {};
        for (const [key, value] of Object.entries(widgetMapping)) {
            effectiveWidgetMapping[value] = key;
        }
    }

    oldNode.widgets.forEach(widget => {
        const newName = effectiveWidgetMapping[widget.name];
        if (newName) {
            const newWidget = findWidgetByName(newNode, newName);
            if (newWidget) {
                newWidget.value = widget.value;
            }
        }
    });

    // Hardcoded transfer for specific outputs based on the output names from the nodes in the image
    const outputMapping = {
        "MODEL": null,           // Not present in "Eff. Loader SDXL"
        "CONDITIONING+": null,   // Not present in "Eff. Loader SDXL"
        "CONDITIONING-": null,   // Not present in "Eff. Loader SDXL"
        "LATENT": "LATENT",
        "VAE": "VAE",
        "CLIP": null,            // Not present in "Eff. Loader SDXL"
        "DEPENDENCIES": "DEPENDENCIES"
    };

    // Transfer connections from old node outputs to new node outputs based on the outputMapping
    oldNode.outputs.forEach((output, index) => {
        if (output && output.links && outputMapping[output.name]) {
            const newOutputName = outputMapping[output.name];
            
            // If the new node does not have this output, skip
            if (newOutputName === null) {
                return;
            }
            
            const newOutputIndex = newNode.findOutputSlot(newOutputName);
            if (newOutputIndex !== -1) {
                output.links.forEach(link => {
                    const targetLinkInfo = oldNode.graph.links[link];
                    if (targetLinkInfo) {
                        const targetNode = oldNode.graph.getNodeById(targetLinkInfo.target_id);
                        if (targetNode) {
                            newNode.connect(newOutputIndex, targetNode, targetLinkInfo.target_slot);
                        }
                    }
                });
            }
        }
    });

    // Remove old node
    app.graph.remove(oldNode);
}

function replaceNodeMenuCallback(currentNode, targetNodeName) {
    return function() {
        replaceNode(currentNode, targetNodeName);
    };
}

function showSwapMenu(value, options, e, menu, node) {
    const swapOptions = [];

    if (node.type !== "Efficient Loader") {
        swapOptions.push({
            content: "Efficient Loader",
            callback: replaceNodeMenuCallback(node, "Efficient Loader")
        });
    }

    if (node.type !== "Eff. Loader SDXL") {
        swapOptions.push({
            content: "Eff. Loader SDXL",
            callback: replaceNodeMenuCallback(node, "Eff. Loader SDXL")
        });
    }

    new LiteGraph.ContextMenu(swapOptions, {
        event: e,
        callback: null,
        parentMenu: menu,
        node: node
    });

    return false;  // This ensures the original context menu doesn't proceed
}

// Extension Definition
app.registerExtension({
    name: "efficiency.SwapLoaders",
    async beforeRegisterNodeDef(nodeType, nodeData, app) {
        if (["Efficient Loader", "Eff. Loader SDXL"].includes(nodeData.name)) {
            addMenuHandler(nodeType, function (insertOption) {
                insertOption({
                    content: "🔄 Swap with...",
                    has_submenu: true,
                    callback: showSwapMenu
                });
            });
        }
    },
});
