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

const LAST_SEED_BUTTON_LABEL = '🎲 Randomize / ♻️ Last Queued Seed';
const SEED_BEHAVIOR_RANDOMIZE = 'Randomize';
const SEED_BEHAVIOR_INCREMENT = 'Increment';
const SEED_BEHAVIOR_DECREMENT = 'Decrement';

const NODE_WIDGET_MAP = {
    "KSampler (Efficient)": "seed",
    "KSampler Adv. (Efficient)": "noise_seed",
    "KSampler SDXL (Eff.)": "noise_seed",
    "Noise Control Script": "seed",
    "HighRes-Fix Script": "seed",
    "Tiled Upscaler Script": "seed"
};

const SPECIFIC_WIDTH = 325; // Set to desired width

function setNodeWidthForMappedTitles(node) {
    if (NODE_WIDGET_MAP[node.getTitle()]) {
        node.setSize([SPECIFIC_WIDTH, node.size[1]]);
    }
}

class SeedControl {
    constructor(node, seedName) {
        this.lastSeed = -1;
        this.serializedCtx = {};
        this.node = node;
        this.seedBehavior = 'randomize'; // Default behavior

        let controlAfterGenerateIndex;

        for (const [i, w] of this.node.widgets.entries()) {
            if (w.name === seedName) {
                this.seedWidget = w;
            } else if (w.name === 'control_after_generate') {
                controlAfterGenerateIndex = i;
                this.node.widgets.splice(i, 1);
            }
        }

        if (!this.seedWidget) {
            throw new Error('Something\'s wrong; expected seed widget');
        }

        this.lastSeedButton = this.node.addWidget("button", LAST_SEED_BUTTON_LABEL, null, () => {
            const isValidValue = Number.isInteger(this.seedWidget.value) && this.seedWidget.value >= min && this.seedWidget.value <= max;
            
            // Special case: if the current label is the default and seed value is -1
            if (this.lastSeedButton.name === LAST_SEED_BUTTON_LABEL && this.seedWidget.value == -1) {
                return; // Do nothing and return early
            }
            
            if (isValidValue && this.seedWidget.value != -1) {
                this.lastSeed = this.seedWidget.value;
                this.seedWidget.value = -1;
            } else if (this.lastSeed !== -1) {
                this.seedWidget.value = this.lastSeed;
            } else {
                this.seedWidget.value = -1; // Set to -1 if the label didn't update due to a seed value issue
            }
            
            if (isValidValue) {
                this.updateButtonLabel(); // Update the button label to reflect the change
            }
        }, { width: 50, serialize: false });

        setNodeWidthForMappedTitles(node);
        if (controlAfterGenerateIndex !== undefined) {
            const addedWidget = this.node.widgets.pop();
            this.node.widgets.splice(controlAfterGenerateIndex, 0, addedWidget);
            setNodeWidthForMappedTitles(node);
        }

        const max = Math.min(1125899906842624, this.seedWidget.options.max);
        const min = Math.max(-1125899906842624, this.seedWidget.options.min);
        const range = (max - min) / (this.seedWidget.options.step / 10);

        this.seedWidget.serializeValue = async (node, index) => {
            // Check if the button is disabled
            if (this.lastSeedButton.disabled) {
                return this.seedWidget.value;
            }

            const currentSeed = this.seedWidget.value;
            this.serializedCtx = {
                wasSpecial: currentSeed == -1,
            };

            if (this.serializedCtx.wasSpecial) {
                switch (this.seedBehavior) {
                    case 'increment':
                        this.serializedCtx.seedUsed = this.lastSeed + 1;
                        break;
                    case 'decrement':
                        this.serializedCtx.seedUsed = this.lastSeed - 1;
                        break;
                    default:
                        this.serializedCtx.seedUsed = Math.floor(Math.random() * range) * (this.seedWidget.options.step / 10) + min;
                        break;
                }

            // Ensure the seed value is an integer and remains within the accepted range
            this.serializedCtx.seedUsed = Number.isInteger(this.serializedCtx.seedUsed) ? Math.min(Math.max(this.serializedCtx.seedUsed, min), max) : this.seedWidget.value;

            } else {
                this.serializedCtx.seedUsed = this.seedWidget.value;
            }

            if (node && node.widgets_values) {
                node.widgets_values[index] = this.serializedCtx.seedUsed;
            } else {
                // Update the last seed value and the button's label to show the current seed value
                this.lastSeed = this.serializedCtx.seedUsed;
                this.updateButtonLabel();
            }

            this.seedWidget.value = this.serializedCtx.seedUsed;

            if (this.serializedCtx.wasSpecial) {
                this.lastSeed = this.serializedCtx.seedUsed;
                this.updateButtonLabel();
            }

            return this.serializedCtx.seedUsed;
        };

        this.seedWidget.afterQueued = () => {
            // Check if the button is disabled
            if (this.lastSeedButton.disabled) {
                return; // Exit the function immediately
            }

            if (this.serializedCtx.wasSpecial) {
                this.seedWidget.value = -1;
            }

            // Check if seed has changed to a non -1 value, and if so, update lastSeed
            if (this.seedWidget.value !== -1) {
                this.lastSeed = this.seedWidget.value;
            }

            this.updateButtonLabel();
            this.serializedCtx = {};
        };
    }

    setBehavior(behavior) {
        this.seedBehavior = behavior;

        // Capture the current seed value as lastSeed and then set the seed widget value to -1
        if (this.seedWidget.value != -1) {
            this.lastSeed = this.seedWidget.value;
            this.seedWidget.value = -1;
        }

        this.updateButtonLabel();
    }

    updateButtonLabel() {

        switch (this.seedBehavior) {
            case 'increment':
                this.lastSeedButton.name = `➕ Increment / ♻️ ${this.lastSeed === -1 ? "Last Queued Seed" : this.lastSeed}`;
                break;
            case 'decrement':
                this.lastSeedButton.name = `➖ Decrement / ♻️ ${this.lastSeed === -1 ? "Last Queued Seed" : this.lastSeed}`;
                break;
            default:
                this.lastSeedButton.name = `🎲 Randomize / ♻️ ${this.lastSeed === -1 ? "Last Queued Seed" : this.lastSeed}`;
                break;
        }
    }

}

function showSeedBehaviorMenu(value, options, e, menu, node) {
    const behaviorOptions = [
        {
            content: "🎲 Randomize",
            callback: () => {
                node.seedControl.setBehavior('randomize');
            }
        },
        {
            content: "➕ Increment",
            callback: () => {
                node.seedControl.setBehavior('increment');
            }
        },
        {
            content: "➖ Decrement",
            callback: () => {
                node.seedControl.setBehavior('decrement');
            }
        }
    ];

    new LiteGraph.ContextMenu(behaviorOptions, {
        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.seedcontrol",
    async beforeRegisterNodeDef(nodeType, nodeData, _app) {
        if (NODE_WIDGET_MAP[nodeData.name]) {
            addMenuHandler(nodeType, function (insertOption) {
                // Check conditions before showing the seed behavior option
                let showSeedOption = true;

                if (nodeData.name === "Noise Control Script") {
                    // Check for 'add_seed_noise' widget being false
                    const addSeedNoiseWidget = this.widgets.find(w => w.name === 'add_seed_noise');
                    if (addSeedNoiseWidget && !addSeedNoiseWidget.value) {
                        showSeedOption = false;
                    }
                } else if (nodeData.name === "HighRes-Fix Script") {
                    // Check for 'use_same_seed' widget being true
                    const useSameSeedWidget = this.widgets.find(w => w.name === 'use_same_seed');
                    if (useSameSeedWidget && useSameSeedWidget.value) {
                        showSeedOption = false;
                    }
                }

                if (showSeedOption) {
                    insertOption({
                        content: "🌱 Seed behavior...",
                        has_submenu: true,
                        callback: showSeedBehaviorMenu
                    });
                }
            });
            
            const onNodeCreated = nodeType.prototype.onNodeCreated;
            nodeType.prototype.onNodeCreated = function () {
                onNodeCreated ? onNodeCreated.apply(this, []) : undefined;
                this.seedControl = new SeedControl(this, NODE_WIDGET_MAP[nodeData.name]);
                this.seedControl.seedWidget.value = -1;
            };
        }
    },
});
