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

app.registerExtension({
    name: "Comfy.LoadAudioUI",
    async beforeRegisterNodeDef(nodeType, nodeData, app) {
        if (nodeData.name === "LoadAudioUI") {
            const onNodeCreated = nodeType.prototype.onNodeCreated;
            const onDrawBackground = nodeType.prototype.onDrawBackground;
            
            // --- V1 LiteGraph Image Preview Hider ---
            nodeType.prototype.onDrawBackground = function (ctx) {
                if (onDrawBackground) {
                    onDrawBackground.apply(this, arguments);
                }
            };
            
            nodeType.prototype.onNodeCreated = function () {
                // Ensure standard creation logic runs first
                const r = onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined;
                const node = this; // Capture the node instance
                
                // Track if we are in the initial loading phase to prevent resetting saved trim values
                node._initializing = true;
                node._should_reset_trim = false;
				               
                // ====================================================================
                // FIX: HIDE DEFAULT COMFYUI AUDIO PLAYER SAFELY
                // ====================================================================
                setTimeout(() => {
                    if (node.widgets) {
                        const nativeWidgetIndex = node.widgets.findIndex(w => w.name === "audioUI");
                        if (nativeWidgetIndex !== -1) {
                            const w = node.widgets[nativeWidgetIndex];
                            if (w.element) {
                                w.element.style.display = "none";
                                w.element.style.height = "0px";
                                w.element.style.position = "absolute";
                                w.element.style.pointerEvents = "none";
                            }
                            w.type = "hidden";
                            w.hidden = true;
                            w.computeSize = () => [0, 0];
                            
                            // Only update height to account for the hidden widget, 
                            // preserving the width (whether it's the default 475 or a user-saved value).
                            const currentWidth = node.size[0];
                            const recommendedHeight = node.computeSize()[1];
                            node.setSize([currentWidth, recommendedHeight]);
                            
                            if (app.graph) {
                                app.graph.setDirtyCanvas(true, true);
                            }
                        }
                    }
                }, 10);
                // ====================================================================

				
                // --- THE CORE FIX FOR COMFYUI V2 ---
                Object.defineProperty(node, 'imgs', {
                    get: function() { return undefined; },
                    set: function(val) { /* Ignore attempts by ComfyUI to set an image preview */ },
                    configurable: true
                });

                // Shared upload handler
                const handleFileUpload = async (file) => {
                    if (!file.type.startsWith('audio/') && !file.type.startsWith('video/')) return false;
                    try {
                        const body = new FormData();
                        body.append("image", file);
                        body.append("type", "input");
                        body.append("subfolder", "");
                        
                        const resp = await api.fetchApi("/upload/image", {
                            method: "POST",
                            body,
                        });

                        if (resp.status === 200) {
                            const data = await resp.json();
                            const audioWidget = node.widgets && node.widgets.find(w => w.name === "audio");
                            if (audioWidget) {
                                // Manual upload should always reset the trim range
                                node._should_reset_trim = true;
                                audioWidget.value = data.name;
                                if (audioWidget.options && audioWidget.options.values && !audioWidget.options.values.includes(data.name)) {
                                    audioWidget.options.values.push(data.name);
                                }
                                if (audioWidget.callback) {
                                    audioWidget.callback(data.name);
                                }
                                app.graph.setDirtyCanvas(true, false);
                            }
                        }
                    } catch (err) {
                        console.error("Error uploading dragged audio file:", err);
                    }
                    return true;
                };

                this.onDragDrop = function(e) {
                    if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length > 0) {
                        const file = e.dataTransfer.files[0];
                        if (file.type.startsWith('audio/') || file.type.startsWith('video/')) {
                            handleFileUpload(file);
                            return true;
                        }
                    }
                    return false;
                };

                // 1. Build the Main Custom HTML Container
                const container = document.createElement("div");
                const defaultBg = "rgba(30, 30, 30, 0.9)";
                Object.assign(container.style, {
                    display: "flex",
                    flexDirection: "column",
                    gap: "10px", 
                    width: "100%",
                    padding: "10px", 
                    boxSizing: "border-box",
                    background: defaultBg,
                    borderRadius: "6px",
                    color: "white",
                    fontFamily: "sans-serif",
                    marginTop: "8px",
                    flexShrink: "0",
                    transition: "background 0.2s"
                });

                const playerTop = document.createElement("div");
                Object.assign(playerTop.style, {
                    display: "flex",
                    justifyContent: "space-between",
                    alignItems: "center",
                    padding: "0 2px",
                    marginBottom: "-4px"
                });
                
                const playerTitle = document.createElement("span");
                playerTitle.textContent = "No audio selected";
                Object.assign(playerTitle.style, {
                    fontSize: "11px",
                    color: "#aaa",
                    whiteSpace: "nowrap",
                    overflow: "hidden",
                    textOverflow: "ellipsis",
                    maxWidth: "140px"
                });

                const trimLength = document.createElement("span");
                Object.assign(trimLength.style, {
                    fontSize: "11px",
                    color: "#38bdf8",
                    fontWeight: "bold",
                    background: "rgba(56, 189, 248, 0.1)",
                    padding: "3px 6px",
                    borderRadius: "4px",
                    whiteSpace: "nowrap"
                });
                trimLength.textContent = "Trimmed: 0.0s";

                playerTop.appendChild(playerTitle);
                playerTop.appendChild(trimLength);
                container.appendChild(playerTop);

                const audioEl = document.createElement("audio");
                audioEl.controls = true;
                audioEl.style.width = "100%";
                audioEl.style.height = "40px";
                audioEl.style.outline = "none";
                container.appendChild(audioEl);

                const trimArea = document.createElement("div");
                Object.assign(trimArea.style, {
                    display: "flex",
                    flexDirection: "column",
                    gap: "6px",
                    background: "rgba(0, 0, 0, 0.35)",
                    padding: "12px",
                    borderRadius: "6px",
                    border: "1px solid rgba(255, 255, 255, 0.05)"
                });

                const timeRuler = document.createElement("div");
                Object.assign(timeRuler.style, {
                    position: "relative",
                    width: "100%",
                    height: "22px",
                    fontSize: "10px",
                    color: "#aaa",
                    pointerEvents: "none",
                    userSelect: "none"
                });
                trimArea.appendChild(timeRuler);

                const sliderBox = document.createElement("div");
                Object.assign(sliderBox.style, {
                    position: "relative",
                    width: "100%",
                    height: "24px",
                    background: "#111",
                    borderRadius: "4px",
                    cursor: "pointer",
                    userSelect: "none",
                    boxShadow: "inset 0 1px 3px rgba(0,0,0,0.5)"
                });

                const fill = document.createElement("div");
                Object.assign(fill.style, {
                    position: "absolute",
                    height: "100%",
                    background: "rgba(14, 165, 233, 0.35)",
                    pointerEvents: "none"
                });
                sliderBox.appendChild(fill);

                const createHandle = (color) => {
                    const h = document.createElement("div");
                    Object.assign(h.style, {
                        position: "absolute",
                        top: "0",
                        width: "8px",
                        height: "100%",
                        background: color,
                        transform: "translateX(-50%)",
                        pointerEvents: "none",
                        boxShadow: "0 0 4px rgba(0,0,0,0.8)",
                        borderRadius: "2px"
                    });
                    return h;
                };

                const startHandle = createHandle("#38bdf8");
                const endHandle = createHandle("#38bdf8");
                sliderBox.appendChild(startHandle);
                sliderBox.appendChild(endHandle);
                trimArea.appendChild(sliderBox);
                
                container.appendChild(trimArea);

                // 4. Attach container to the node UI
                const widget = this.addDOMWidget("audio_ui", "audio_ui", container);
                
                // --- DEFAULT SIZE FOR NEW NODES ---
                this.size = [475, this.computeSize()[1]];
                
                widget.computeSize = function(width) {
                    return [width, 200];
                };

                // 5. Bind Node Data to UI dynamically 
                setTimeout(() => {
                    const audioWidget = node.widgets && node.widgets.find(w => w.name === "audio");
                    const startWidget = node.widgets && node.widgets.find(w => w.name === "start_time");
                    const endWidget = node.widgets && node.widgets.find(w => w.name === "end_time");
                    const durationWidget = node.widgets && node.widgets.find(w => w.name === "duration");
                    
                    let duration = 0;
                    let dragging = null;
                    let dragOffset = 0;
                    let dragSelectionWidth = 0;
                    let isUpdatingDuration = false; // Flag to prevent infinite loops

                    // Hook into the Python-generated native duration widget
                    if (durationWidget) {
                        const origCallback = durationWidget.callback;
                        durationWidget.callback = function(v) {
                            // If we're internally triggering it, ignore it
                            if (!duration || isUpdatingDuration) {
                                if (origCallback) origCallback.apply(this, arguments);
                                return;
                            }
                            
                            isUpdatingDuration = true;
                            let d = parseFloat(v) || 0;
                            if (d < 0) d = 0;
                            if (d > duration) d = duration;

                            let s = startWidget ? parseFloat(startWidget.value) || 0 : 0;
                            let newStart = s;
                            let newEnd = s + d;

                            // If adding duration pushes past the end of the audio, shift the start time back instead
                            if (newEnd > duration) {
                                newEnd = duration;
                                newStart = duration - d;
                            }

                            if (startWidget) startWidget.value = parseFloat(newStart.toFixed(2));
                            if (endWidget) endWidget.value = parseFloat(newEnd.toFixed(2));

                            updateUI(true);
                            app.graph.setDirtyCanvas(true, false);
                            
                            if (origCallback) origCallback.apply(this, arguments);
                            isUpdatingDuration = false;
                        };
                    }
                    
                    if (audioWidget) {
                        const updateAudio = () => {
                            if (!audioWidget.value || audioWidget.value === "none") {
                                playerTitle.textContent = "No audio selected";
                                return;
                            }
                            let filename = audioWidget.value;
                            let subfolder = "";
                            if (filename.includes("/") || filename.includes("\\")) {
                                const sep = filename.includes("/") ? "/" : "\\";
                                const parts = filename.split(sep);
                                filename = parts.pop();
                                subfolder = parts.join("/");
                            }
                            playerTitle.textContent = filename;
                            audioEl.src = api.apiURL(`/view?filename=${encodeURIComponent(filename)}&type=input&subfolder=${encodeURIComponent(subfolder)}`);
                        };
                        audioWidget.callback = function() {
                            // If user manually changes the dropdown, flag for trim reset
                            if (!node._initializing) {
                                node._should_reset_trim = true;
                            }
                            updateAudio();
                        };
                        updateAudio();
                    }

                    container.ondragover = (e) => {
                        e.preventDefault();
                        e.stopPropagation();
                        container.style.background = "rgba(14, 165, 233, 0.2)";
                    };
                    container.ondragleave = (e) => {
                        e.preventDefault();
                        e.stopPropagation();
                        container.style.background = defaultBg;
                    };
                    container.ondrop = async (e) => {
                        e.preventDefault();
                        e.stopPropagation();
                        container.style.background = defaultBg;
                        if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
                            handleFileUpload(e.dataTransfer.files[0]);
                        }
                    };

                    const formatTime = (secs) => {
                        if (secs < 60) return secs.toFixed(1) + "s";
                        const m = Math.floor(secs / 60);
                        const s = (secs % 60).toFixed(1);
                        return `${m}:${s.padStart(4, '0')}`;
                    };

                    const updateRuler = () => {
                        timeRuler.innerHTML = '';
                        if (!duration) return;
                        const numMajorTicks = 5;
                        const subTicks = 4;
                        const totalTicks = (numMajorTicks - 1) * subTicks; 
                        for (let i = 0; i <= totalTicks; i++) {
                            const pct = i / totalTicks;
                            const t = duration * pct;
                            const isMajor = i % subTicks === 0;
                            const tickWrapper = document.createElement("div");
                            Object.assign(tickWrapper.style, {
                                position: "absolute", left: `${pct * 100}%`, top: "0",
                                display: "flex", flexDirection: "column", alignItems: "center", transform: "translateX(-50%)"
                            });
                            if (i === 0) { tickWrapper.style.transform = "none"; tickWrapper.style.alignItems = "flex-start"; }
                            if (i === totalTicks) { tickWrapper.style.transform = "translateX(-100%)"; tickWrapper.style.alignItems = "flex-end"; }
                            const line = document.createElement("div");
                            Object.assign(line.style, {
                                width: isMajor ? "2px" : "1px", height: isMajor ? "6px" : "4px",
                                background: isMajor ? "#aaa" : "#555", marginBottom: "2px", borderRadius: "1px"
                            });
                            tickWrapper.appendChild(line);
                            if (isMajor) {
                                const label = document.createElement("div");
                                label.textContent = formatTime(t);
                                tickWrapper.appendChild(label);
                            }
                            timeRuler.appendChild(tickWrapper);
                        }
                    };
                    
                    const updateUI = (syncPlayer = false) => {
                        if (!duration) return;
                        let s = startWidget ? parseFloat(startWidget.value) || 0 : 0;
                        let e = endWidget ? parseFloat(endWidget.value) || 0 : 0;
                        if (e === 0 || e > duration) e = duration;
                        if (s > e) s = e;
                        const sPct = (s / duration) * 100;
                        const ePct = (e / duration) * 100;
                        startHandle.style.left = `${sPct}%`;
                        endHandle.style.left = `${ePct}%`;
                        fill.style.left = `${sPct}%`;
                        fill.style.width = `${ePct - sPct}%`;
                        
                        // Sync native duration widget seamlessly to match UI handles
                        const currentDur = parseFloat((e - s).toFixed(2));
                        trimLength.textContent = `Trimmed: ${currentDur}s`;
                        if (durationWidget && durationWidget.value !== currentDur) {
                            isUpdatingDuration = true;
                            durationWidget.value = currentDur;
                            isUpdatingDuration = false;
                        }
                        
                        if (syncPlayer && audioEl.readyState >= 1) { audioEl.currentTime = s; }
                    };

                    audioEl.onloadedmetadata = () => {
                        duration = audioEl.duration;
                        
                        // Handle trim reset for new audio selection
                        if (node._should_reset_trim) {
                            if (startWidget) startWidget.value = 0;
                            if (endWidget) endWidget.value = parseFloat(duration.toFixed(2));
                            node._should_reset_trim = false;
                        } else {
                            // Default clamping logic for initial load or out-of-bounds saved values
                            let e = endWidget ? parseFloat(endWidget.value) || 0 : 0;
                            if (endWidget && (e === 0 || e > duration)) { 
                                endWidget.value = parseFloat(duration.toFixed(2)); 
                            }
                        }
                        
                        updateRuler(); 
                        updateUI();
                        app.graph.setDirtyCanvas(true, false);
                    };

                    audioEl.ontimeupdate = () => {
                        if (dragging || !duration) return;
                        let s = startWidget ? parseFloat(startWidget.value) || 0 : 0;
                        let e = endWidget ? parseFloat(endWidget.value) || duration : duration;
                        if (e === 0) e = duration;
                        if (audioEl.currentTime >= e) { audioEl.pause(); audioEl.currentTime = s; }
                    };

                    audioEl.onplay = () => {
                        let s = startWidget ? parseFloat(startWidget.value) || 0 : 0;
                        let e = endWidget ? parseFloat(endWidget.value) || duration : duration;
                        if (e === 0) e = duration;
                        if (audioEl.currentTime < s || audioEl.currentTime >= e) { audioEl.currentTime = s; }
                    };

                    [startWidget, endWidget].forEach(w => {
                        if (w) {
                            const orig = w.callback;
                            w.callback = function() { updateUI(true); if(orig) orig.apply(this, arguments); };
                        }
                    });

                    sliderBox.onpointerdown = (e) => {
                        if (!duration) return;
                        const rect = sliderBox.getBoundingClientRect();
                        const x = Math.max(0, Math.min(e.clientX - rect.left, rect.width));
                        const val = (x / rect.width) * duration;
                        let s = startWidget ? parseFloat(startWidget.value) || 0 : 0;
                        let e_val = endWidget ? parseFloat(endWidget.value) || duration : duration;
                        
                        // Define a "handle tolerance" zone (approx 10px on each side) to prioritize resizing over dragging
                        const handleTolerance = (10 / rect.width) * duration;
                        
                        if (val > s + handleTolerance && val < e_val - handleTolerance) {
                            dragging = 'center';
                            dragOffset = val - s;
                            dragSelectionWidth = e_val - s;
                        } else if (Math.abs(val - s) < Math.abs(val - e_val)) {
                            dragging = 'start';
                            if(startWidget) startWidget.value = parseFloat(Math.min(val, e_val).toFixed(2));
                        } else {
                            dragging = 'end';
                            if(endWidget) endWidget.value = parseFloat(Math.max(val, s).toFixed(2));
                        }
                        updateUI(true); app.graph.setDirtyCanvas(true, false);
                        sliderBox.setPointerCapture(e.pointerId);
                    };

                    sliderBox.onpointermove = (e) => {
                        if (!dragging || !duration) return;
                        const rect = sliderBox.getBoundingClientRect();
                        const x = Math.max(0, Math.min(e.clientX - rect.left, rect.width));
                        const val = (x / rect.width) * duration;
                        if (dragging === 'start') {
                            let e_val = endWidget ? parseFloat(endWidget.value) || duration : duration;
                            if(startWidget) startWidget.value = parseFloat(Math.min(val, e_val).toFixed(2));
                        } else if (dragging === 'end') {
                            const s = startWidget ? parseFloat(startWidget.value) || 0 : 0;
                            if(endWidget) endWidget.value = parseFloat(Math.max(val, s).toFixed(2));
                        } else if (dragging === 'center') {
                            let newStart = val - dragOffset;
                            let newEnd = newStart + dragSelectionWidth;
                            
                            // Clamp to bounds
                            if (newStart < 0) {
                                newStart = 0;
                                newEnd = dragSelectionWidth;
                            } else if (newEnd > duration) {
                                newEnd = duration;
                                newStart = duration - dragSelectionWidth;
                            }
                            
                            if(startWidget) startWidget.value = parseFloat(newStart.toFixed(2));
                            if(endWidget) endWidget.value = parseFloat(newEnd.toFixed(2));
                        }
                        updateUI(true); app.graph.setDirtyCanvas(true, false);
                    };

                    sliderBox.onpointerup = (e) => { dragging = null; sliderBox.releasePointerCapture(e.pointerId); };

                    // Exit initialization phase
                    setTimeout(() => { node._initializing = false; }, 500);

                }, 100);
                return r;
            }
        }
    }
});