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

const connectionMap = {
    "KSampler (Efficient)": ["input", 5],
    "KSampler Adv. (Efficient)": ["input", 5],
    "KSampler SDXL (Eff.)": ["input", 3],
    "XY Plot": ["output", 0],
    "Noise Control Script": ["input & output", 0],
    "HighRes-Fix Script": ["input & output", 0],
    "Tiled Upscaler Script": ["input & output", 0],
    "AnimateDiff Script": ["output", 0]
};

    /**
     * connect this node output to the input of another node
     * @method connect
     * @param {number_or_string} slot (could be the number of the slot or the string with the name of the slot)
     * @param {LGraphNode} node the target node
     * @param {number_or_string} target_slot the input slot of the target node (could be the number of the slot or the string with the name of the slot, or -1 to connect a trigger)
     * @return {Object} the link_info is created, otherwise null
    LGraphNode.prototype.connect = function(output_slot, target_node, input_slot)
    **/

function addAndConnectScriptNode(scriptType, selectedNode) {
    const selectedNodeType = connectionMap[selectedNode.type];
    const newNodeType = connectionMap[scriptType];

    // 1. Create the new node without position adjustments
    const newNode = addNode(scriptType, selectedNode, { shiftX: 0, shiftY: 0 });

    // 2. Adjust position of the new node based on conditions
    if (newNodeType[0].includes("input") && selectedNodeType[0].includes("output")) {
        newNode.pos[0] += selectedNode.size[0] + 50;
    } else if (newNodeType[0].includes("output") && selectedNodeType[0].includes("input")) {
        newNode.pos[0] -= (newNode.size[0] + 50);
    }

    // 3. Logic for connecting the nodes
    switch (selectedNodeType[0]) {
        case "output":
            if (newNodeType[0] === "input & output") {
                // For every node that was previously connected to the selectedNode's output
                const connectedNodes = selectedNode.getOutputNodes(selectedNodeType[1]);
                if (connectedNodes && connectedNodes.length) {
                    for (let connectedNode of connectedNodes) {
                        // Disconnect the node from selectedNode's output
                        selectedNode.disconnectOutput(selectedNodeType[1]);
                        // Connect the newNode's output to the previously connected node, 
                        // using the appropriate slot based on the type of the connectedNode
                        const targetSlot = (connectedNode.type in connectionMap) ? connectionMap[connectedNode.type][1] : 0;
                        newNode.connect(0, connectedNode, targetSlot);
                    }
                }
                // Connect selectedNode's output to newNode's input
                selectedNode.connect(selectedNodeType[1], newNode, newNodeType[1]);
            }
            break;

        case "input":
            if (newNodeType[0] === "output") {
                newNode.connect(0, selectedNode, selectedNodeType[1]);
            } else if (newNodeType[0] === "input & output") {
                const ogInputNode = selectedNode.getInputNode(selectedNodeType[1]);
                if (ogInputNode) {
                    ogInputNode.connect(0, newNode, 0);
                }
                newNode.connect(0, selectedNode, selectedNodeType[1]);
            }
            break;
        case "input & output":
            if (newNodeType[0] === "output") {
                newNode.connect(0, selectedNode, 0);
            } else if (newNodeType[0] === "input & output") {

                const connectedNodes = selectedNode.getOutputNodes(0);
                if (connectedNodes && connectedNodes.length) {
                    for (let connectedNode of connectedNodes) {
                        selectedNode.disconnectOutput(0);
                        newNode.connect(0, connectedNode, connectedNode.type in connectionMap ? connectionMap[connectedNode.type][1] : 0);
                    }
                }
                // Connect selectedNode's output to newNode's input
                selectedNode.connect(selectedNodeType[1], newNode, newNodeType[1]);
            }
            break;
    }

    return newNode;
}

function createScriptEntry(node, scriptType) {
    return {
        content: scriptType,
        callback: function() {
            addAndConnectScriptNode(scriptType, node);
        },
    };
}

function getScriptOptions(nodeType, node) {
    const allScriptTypes = [
        "XY Plot",
        "Noise Control Script",
        "HighRes-Fix Script",
        "Tiled Upscaler Script",
        "AnimateDiff Script"
    ];

    // Filter script types based on node type
    const scriptTypes = allScriptTypes.filter(scriptType => {
        const scriptBehavior = connectionMap[scriptType][0];
        
        if (connectionMap[nodeType][0] === "output") {
            return scriptBehavior.includes("input");  // Includes nodes that are "input" or "input & output"
        } else {
            return true;
        }
    });

    return scriptTypes.map(script => createScriptEntry(node, script));
}


function showAddScriptMenu(_, options, e, menu, node) {
    const scriptOptions = getScriptOptions(node.type, node);
    new LiteGraph.ContextMenu(scriptOptions, {
        event: e,
        callback: null,
        parentMenu: menu,
        node: node
    });
    return false;
}

// Extension Definition
app.registerExtension({
    name: "efficiency.addScripts",
    async beforeRegisterNodeDef(nodeType, nodeData, app) {
        if (connectionMap[nodeData.name]) {
            addMenuHandler(nodeType, function(insertOption) {
                insertOption({
                    content: "📜 Add script...",
                    has_submenu: true,
                    callback: showAddScriptMenu
                });
            });
        }
    },
});

