/**
 * MediaUtils.js
 * Media-specific utility functions for showcase components
 * (Moved from uiHelpers.js to better organize code)
 */
import { showToast, copyToClipboard, getNSFWLevelName, sendPromptToWorkflow, stripLoraTags, sendGenParamsToWorkflow } from '../../../utils/uiHelpers.js';
import { state } from '../../../state/index.js';
import { getModelApiClient } from '../../../api/modelApiFactory.js';
import { NSFW_LEVELS, getMatureBlurThreshold } from '../../../utils/constants.js';
import { getNsfwLevelSelector } from '../NsfwLevelSelector.js';

/**
 * Try to load local image first, fall back to remote if local fails
 * @param {HTMLImageElement} imgElement - The image element to update
 * @param {Object} urls - Object with local URLs {primary, fallback} and remote URL
 */
export function tryLocalImageOrFallbackToRemote(imgElement, urls) {
    const { primary: localUrl, fallback: fallbackUrl } = urls.local || {};
    const remoteUrl = urls.remote;
    
    // If no local options, use remote directly
    if (!localUrl) {
        imgElement.src = remoteUrl;
        return;
    }
    
    // Try primary local URL
    const testImg = new Image();
    testImg.onload = () => {
        // Primary local image loaded successfully
        imgElement.src = localUrl;
    };
    testImg.onerror = () => {
        // Try fallback URL if available
        if (fallbackUrl) {
            const fallbackImg = new Image();
            fallbackImg.onload = () => {
                imgElement.src = fallbackUrl;
            };
            fallbackImg.onerror = () => {
                // Both local options failed, use remote
                imgElement.src = remoteUrl;
            };
            fallbackImg.src = fallbackUrl;
        } else {
            // No fallback, use remote
            imgElement.src = remoteUrl;
        }
    };
    testImg.src = localUrl;
}

/**
 * Try to load local video first, fall back to remote if local fails
 * @param {HTMLVideoElement} videoElement - The video element to update
 * @param {Object} urls - Object with local URLs {primary} and remote URL
 */
export function tryLocalVideoOrFallbackToRemote(videoElement, urls) {
    const { primary: localUrl } = urls.local || {};
    const remoteUrl = urls.remote;
    
    // Only try local if we have a local path
    if (localUrl) {
        // Try to fetch local file headers to see if it exists
        fetch(localUrl, { method: 'HEAD' })
            .then(response => {
                if (response.ok) {
                    // Local video exists, use it
                    videoElement.src = localUrl;
                    const source = videoElement.querySelector('source');
                    if (source) source.src = localUrl;
                } else {
                    // Local video doesn't exist, use remote
                    videoElement.src = remoteUrl;
                    const source = videoElement.querySelector('source');
                    if (source) source.src = remoteUrl;
                }
                videoElement.load();
            })
            .catch(() => {
                // Error fetching, use remote
                videoElement.src = remoteUrl;
                const source = videoElement.querySelector('source');
                if (source) source.src = remoteUrl;
                videoElement.load();
            });
    } else {
        // No local path, use remote directly
        videoElement.src = remoteUrl;
        const source = videoElement.querySelector('source');
        if (source) source.src = remoteUrl;
        videoElement.load();
    }
}

/**
 * Initialize lazy loading for images and videos in a container
 * @param {HTMLElement} container - The container with lazy-loadable elements
 */
export function initLazyLoading(container) {
    const lazyElements = container.querySelectorAll('.lazy');
    
    const lazyLoad = (element) => {
        // Get URLs from data attributes
        const localUrls = {
            primary: element.dataset.localSrc || null,
            fallback: element.dataset.localFallbackSrc || null
        };
        const remoteUrl = element.dataset.remoteSrc;
        
        const urls = {
            local: localUrls,
            remote: remoteUrl
        };
        
        // Check if element is a video or image
        if (element.tagName.toLowerCase() === 'video') {
            tryLocalVideoOrFallbackToRemote(element, urls);
        } else {
            tryLocalImageOrFallbackToRemote(element, urls);
        }
        
        element.classList.remove('lazy');
    };

    const observer = new IntersectionObserver((entries) => {
        entries.forEach(entry => {
            if (entry.isIntersecting) {
                lazyLoad(entry.target);
                observer.unobserve(entry.target);
            }
        });
    });

    lazyElements.forEach(element => observer.observe(element));
}

/**
 * Check which Create As Recipe buttons correspond to already-imported
 * images and disable them.
 */
async function checkImportedRecipes(container) {
    const recipeButtons = container.querySelectorAll('.create-recipe-btn');
    if (!recipeButtons.length) return;

    const imageIds = [];
    recipeButtons.forEach(btn => {
        const id = btn.dataset.imageId;
        if (id) imageIds.push(id);
    });
    if (!imageIds.length) return;

    try {
        const response = await fetch(`/api/lm/recipes/check-image-exists?image_ids=${imageIds.join(',')}`);
        const data = await response.json();
        if (!data.success || !data.results) return;
        recipeButtons.forEach(btn => {
            const id = btn.dataset.imageId;
            if (id && data.results[id]?.in_library) {
                btn.title = 'Already imported as recipe';
                btn.classList.add('disabled');
                btn.setAttribute('aria-disabled', 'true');
            }
        });
    } catch (err) {
        console.error('Failed to check imported recipes:', err);
    }
}


/**
 * Get the actual rendered rectangle of a media element with object-fit: contain
 * @param {HTMLElement} mediaElement - The img or video element
 * @param {number} containerWidth - Width of the container
 * @param {number} containerHeight - Height of the container
 * @returns {Object} - Rect with left, top, right, bottom coordinates
 */
export function getRenderedMediaRect(mediaElement, containerWidth, containerHeight) {
    // Get natural dimensions of the media
    const naturalWidth = mediaElement.naturalWidth || mediaElement.videoWidth || mediaElement.clientWidth;
    const naturalHeight = mediaElement.naturalHeight || mediaElement.videoHeight || mediaElement.clientHeight;
    
    if (!naturalWidth || !naturalHeight) {
        // Fallback if dimensions cannot be determined
        return { left: 0, top: 0, right: containerWidth, bottom: containerHeight };
    }
    
    // Calculate aspect ratios
    const containerRatio = containerWidth / containerHeight;
    const mediaRatio = naturalWidth / naturalHeight;
    
    let renderedWidth, renderedHeight, left = 0, top = 0;
    
    // Apply object-fit: contain logic
    if (containerRatio > mediaRatio) {
        // Container is wider than media - will have empty space on sides
        renderedHeight = containerHeight;
        renderedWidth = renderedHeight * mediaRatio;
        left = (containerWidth - renderedWidth) / 2;
    } else {
        // Container is taller than media - will have empty space top/bottom
        renderedWidth = containerWidth;
        renderedHeight = renderedWidth / mediaRatio;
        top = (containerHeight - renderedHeight) / 2;
    }
    
    return {
        left,
        top,
        right: left + renderedWidth,
        bottom: top + renderedHeight
    };
}

/**
 * Initialize metadata panel interaction handlers: hover over the media reveals
 * the panel and media controls (same as the legacy carousel). Panel-internal
 * buttons and wheel isolation are bound here as well.
 * @param {HTMLElement} container - Container element with media wrappers
 */
export function initMetadataPanelHandlers(container) {
    const mediaWrappers = container.querySelectorAll('.media-wrapper');

    mediaWrappers.forEach(wrapper => {
        const metadataPanel = wrapper.querySelector('.image-metadata-panel');
        if (!metadataPanel) return;

        const mediaControls = wrapper.querySelector('.media-controls');
        const mediaElement = wrapper.querySelector('img, video');

        if (mediaElement) {
            let isOverMetadataPanel = false;

            // Hovering the actual media content reveals the metadata panel and controls
            wrapper.addEventListener('mousemove', (e) => {
                const rect = wrapper.getBoundingClientRect();
                const mouseX = e.clientX - rect.left;
                const mouseY = e.clientY - rect.top;

                const mediaRect = getRenderedMediaRect(mediaElement, rect.width, rect.height);
                const isOverMedia = (
                    mouseX >= mediaRect.left &&
                    mouseX <= mediaRect.right &&
                    mouseY >= mediaRect.top &&
                    mouseY <= mediaRect.bottom
                );

                if (isOverMedia || isOverMetadataPanel) {
                    metadataPanel.classList.add('visible');
                    if (mediaControls) mediaControls.classList.add('visible');
                } else {
                    metadataPanel.classList.remove('visible');
                    if (mediaControls) mediaControls.classList.remove('visible');
                }
            });

            wrapper.addEventListener('mouseleave', () => {
                if (!isOverMetadataPanel) {
                    metadataPanel.classList.remove('visible');
                    if (mediaControls) mediaControls.classList.remove('visible');
                }
            });

            metadataPanel.addEventListener('mouseenter', () => {
                isOverMetadataPanel = true;
                metadataPanel.classList.add('visible');
                if (mediaControls) mediaControls.classList.add('visible');
            });

            metadataPanel.addEventListener('mouseleave', () => {
                isOverMetadataPanel = false;
                metadataPanel.classList.remove('visible');
                if (mediaControls) mediaControls.classList.remove('visible');
            });
        }

        // Prevent events from bubbling
        metadataPanel.addEventListener('click', (e) => {
            e.stopPropagation();
        });

        // Handle copy prompt buttons
        const copyBtns = metadataPanel.querySelectorAll('.copy-prompt-btn');
        copyBtns.forEach(copyBtn => {
            const promptIndex = copyBtn.dataset.promptIndex;
            const promptElement = wrapper.querySelector(`#prompt-${promptIndex}`);

            copyBtn.addEventListener('click', async (e) => {
                e.stopPropagation();

                if (!promptElement) return;

                try {
                    await copyToClipboard(promptElement.textContent, 'Prompt copied to clipboard');
                } catch (err) {
                    console.error('Copy failed:', err);
                    showToast('toast.triggerWords.copyFailed', {}, 'error');
                }
            });
        });

        // Handle send prompt buttons
        const sendBtns = metadataPanel.querySelectorAll('.send-prompt-btn');
        sendBtns.forEach(sendBtn => {
            const promptIndex = sendBtn.dataset.promptIndex;
            const promptElement = wrapper.querySelector(`#prompt-${promptIndex}`);

            sendBtn.addEventListener('click', async (e) => {
                e.stopPropagation();

                if (!promptElement) return;

                let promptText = promptElement.textContent || '';
                if (!promptText.trim()) {
                    showToast('toast.recipes.noPromptToSend', {}, 'warning');
                    return;
                }

                // Respect strip <lora> setting from global state
                if (state.global.settings?.strip_lora_on_copy) {
                    promptText = stripLoraTags(promptText);
                }

                sendPromptToWorkflow(promptText);
            });
        });

        // Handle send params buttons
        const paramsBtn = metadataPanel.querySelector('.send-params-btn');
        if (paramsBtn) {
            paramsBtn.addEventListener('click', async (e) => {
                e.stopPropagation();

                // Collect gen params from the param-tag elements
                const tagsContainer = wrapper.querySelector('.params-tags');
                if (!tagsContainer) return;

                const paramTags = tagsContainer.querySelectorAll('.param-tag');
                const genParams = {};

                // Map display labels to genParams keys
                const labelToKey = {
                    'Seed': 'seed',
                    'Steps': 'steps',
                    'Sampler': 'sampler',
                    'CFG': 'cfg_scale',
                };

                paramTags.forEach(tag => {
                    const nameEl = tag.querySelector('.param-name');
                    const valueEl = tag.querySelector('.param-value');
                    if (!nameEl || !valueEl) return;

                    const label = nameEl.textContent.replace(':', '').trim();
                    const key = labelToKey[label];
                    if (key) {
                        genParams[key] = valueEl.textContent.trim();
                    }
                });

                if (Object.keys(genParams).length === 0) {
                    showToast('No sendable parameters found', {}, 'warning');
                    return;
                }

                await sendGenParamsToWorkflow(genParams);
            });
        }

        // Prevent panel scroll from causing modal scroll
        metadataPanel.addEventListener('wheel', (e) => {
            const isAtTop = metadataPanel.scrollTop === 0;
            const isAtBottom = metadataPanel.scrollHeight - metadataPanel.scrollTop === metadataPanel.clientHeight;

            // Only prevent default if scrolling would cause the panel to scroll
            if ((e.deltaY < 0 && !isAtTop) || (e.deltaY > 0 && !isAtBottom)) {
                e.stopPropagation();
            }
        }, { passive: true });
    });
}

/**
 * Initialize NSFW content blur toggle handlers
 * @param {HTMLElement} container - Container element with media wrappers
 */
export function initNsfwBlurHandlers(container) {
    // Handle toggle blur buttons
    const toggleButtons = container.querySelectorAll('.toggle-blur-btn');
    toggleButtons.forEach(btn => {
        btn.addEventListener('click', (e) => {
            e.stopPropagation();
            const wrapper = btn.closest('.media-wrapper');
            const media = wrapper.querySelector('img, video');
            const isBlurred = media.classList.toggle('blurred');
            const icon = btn.querySelector('i');
            
            // Update the icon based on blur state
            if (isBlurred) {
                icon.className = 'fas fa-eye';
            } else {
                icon.className = 'fas fa-eye-slash';
            }
            
            // Toggle the overlay visibility
            const overlay = wrapper.querySelector('.nsfw-overlay');
            if (overlay) {
                overlay.style.display = isBlurred ? 'flex' : 'none';
            }
        });
    });
    
    // Handle "Show" buttons in overlays
    const showButtons = container.querySelectorAll('.show-content-btn');
    showButtons.forEach(btn => {
        btn.addEventListener('click', (e) => {
            e.stopPropagation();
            const wrapper = btn.closest('.media-wrapper');
            const media = wrapper.querySelector('img, video');
            media.classList.remove('blurred');
            
            // Update the toggle button icon
            const toggleBtn = wrapper.querySelector('.toggle-blur-btn');
            if (toggleBtn) {
                toggleBtn.querySelector('i').className = 'fas fa-eye-slash';
            }
            
            // Hide the overlay
            const overlay = wrapper.querySelector('.nsfw-overlay');
            if (overlay) {
                overlay.style.display = 'none';
            }
        });
    });
}

/**
 * Initialize media control buttons event handlers
 * @param {HTMLElement} container - Container with media wrappers
 */
export function initMediaControlHandlers(container) {
    // Find all delete buttons in the container
    const deleteButtons = container.querySelectorAll('.example-delete-btn');
    
    deleteButtons.forEach(btn => {
        // Set initial state
        btn.dataset.state = 'initial';
        
        btn.addEventListener('click', async function(e) {
            e.stopPropagation();
            
            // Explicitly check for disabled state
            if (this.classList.contains('disabled')) {
                return; // Don't do anything if button is disabled
            }
            
            const shortId = this.dataset.shortId;
            const btnState = this.dataset.state;
            
            if (!shortId) return;
            
            // Handle two-step confirmation
            if (btnState === 'initial') {
                // First click: show confirmation state
                this.dataset.state = 'confirm';
                this.classList.add('confirm');
                this.title = 'Click again to confirm deletion';
                
                // Auto-reset after 3 seconds
                setTimeout(() => {
                    if (this.dataset.state === 'confirm') {
                        this.dataset.state = 'initial';
                        this.classList.remove('confirm');
                        this.title = 'Delete this example';
                    }
                }, 3000);
                
                return;
            }
            
            // Second click within 3 seconds: proceed with deletion
            if (btnState === 'confirm') {
                this.disabled = true;
                this.classList.remove('confirm');
                this.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
                
                // Get model hash from URL or data attribute
                const mediaWrapper = this.closest('.media-wrapper');
                const modelHashAttr = document.querySelector('.showcase-section')?.dataset;
                const modelHash = modelHashAttr?.modelHash;
                
                try {
                    // Call the API to delete the custom example
                    const response = await fetch('/api/lm/delete-example-image', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json'
                        },
                        body: JSON.stringify({
                            model_hash: modelHash,
                            short_id: shortId
                        })
                    });
                    
                    const result = await response.json();
                    
                    if (result.success) {
                        // Let the gallery refresh itself (removes thumbnail + selects a neighbor)
                        mediaWrapper.dispatchEvent(new CustomEvent('example-media-deleted', {
                            bubbles: true,
                            detail: { shortId }
                        }));

                        // Success: remove the media wrapper from the DOM
                        mediaWrapper.style.opacity = '0';
                        mediaWrapper.style.height = '0';
                        mediaWrapper.style.transition = 'opacity 0.3s ease, height 0.3s ease 0.3s';
                        
                        setTimeout(() => {
                            mediaWrapper.remove();
                        }, 600);
                        
                        // Show success toast
                        showToast('toast.exampleImages.deleted', {}, 'success');

                        // Create an update object with only the necessary properties
                        const updateData = {
                            civitai: {
                                customImages: result.custom_images || []
                            }
                        };
                        
                        // Update the item in the virtual scroller
                        state.virtualScroller.updateSingleItem(result.model_file_path, updateData);
                    } else {
                        // Show error message
                        showToast('toast.exampleImages.deleteFailed', { error: result.error }, 'error');
                        
                        // Reset button state
                        this.disabled = false;
                        this.dataset.state = 'initial';
                        this.classList.remove('confirm');
                        this.innerHTML = '<i class="fas fa-trash-alt"></i>';
                        this.title = 'Delete this example';
                    }
                } catch (error) {
                    console.error('Error deleting example image:', error);
                    showToast('toast.exampleImages.deleteFailed', {}, 'error');
                    
                    // Reset button state
                    this.disabled = false;
                    this.dataset.state = 'initial';
                    this.classList.remove('confirm');
                    this.innerHTML = '<i class="fas fa-trash-alt"></i>';
                    this.title = 'Delete this example';
                }
            }
        });
    });
    
    // Create As Recipe buttons
    const recipeButtons = container.querySelectorAll('.create-recipe-btn');
    recipeButtons.forEach(btn => {
        btn.addEventListener('click', async function(e) {
            e.stopPropagation();
            
            // Ignore clicks when disabled
            if (this.classList.contains('disabled')) {
                return;
            }
            
            const imageMetaRaw = this.dataset.imageMeta;
            const imageUrl = this.dataset.imageUrl;
            const imageNsfw = this.dataset.imageNsfw;
            const imgId = this.dataset.imgId || '';
            const localPath = this.dataset.localPath || '';
            const showcaseSection = this.closest('.showcase-section');
            const modelHash = showcaseSection ? showcaseSection.dataset.modelHash : '';
            const modelName = showcaseSection ? showcaseSection.dataset.modelName : '';
            const modelType = showcaseSection ? showcaseSection.dataset.modelType : '';
            
            if (!imageMetaRaw || !modelHash) {
                showToast('toast.recipes.createMissingData', {}, 'error');
                return;
            }
            
            // Show loading state
            const originalHtml = this.innerHTML;
            this.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
            this.disabled = true;
            
            try {
                const imageMeta = JSON.parse(decodeURIComponent(imageMetaRaw));
                
                const response = await fetch('/api/lm/recipes/create-from-example', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({
                        image_data: {
                            meta: imageMeta,
                            url: imageUrl,
                            nsfwLevel: imageNsfw ? parseInt(imageNsfw, 10) : undefined,
                            id: imgId || undefined,
                        },
                        model_hash: modelHash,
                        model_name: modelName || modelHash,
                        model_type: modelType,
                        local_image_path: localPath,
                    }),
                });
                
                const result = await response.json();
                
                if (result.success && result.recipe_id) {
                    showToast('toast.recipes.created', { recipeId: result.recipe_id }, 'success');
                } else {
                    showToast('toast.recipes.createFailed', { error: result.error || 'Unknown error' }, 'error');
                }
            } catch (error) {
                console.error('Failed to create recipe:', error);
                showToast('toast.recipes.createError', { message: error.message }, 'error');
            } finally {
                this.innerHTML = originalHtml;
                this.disabled = false;
            }
        });
    });
    
    // Check which images are already imported as recipes → disable button
    checkImportedRecipes(container);
    
    // Initialize set preview buttons
    initSetPreviewHandlers(container);

    // Initialize NSFW level buttons
    initSetNsfwHandlers(container);
    
    // Media control visibility is handled with pure CSS (.media-wrapper:hover .media-controls)
    // Any click handlers or other functionality can still be added here
}

/**
 * Initialize set preview button handlers
 * @param {HTMLElement} container - Container with media wrappers
 */
function initSetPreviewHandlers(container) {
    const previewButtons = container.querySelectorAll('.set-preview-btn');
    const modelType = state.currentPageType == 'loras' ? 'lora' : 'checkpoint';
    
    previewButtons.forEach(btn => {
        btn.addEventListener('click', async function(e) {
            e.stopPropagation();
            
            // Show loading state
            this.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
            this.disabled = true;
            
            try {
                // Get the model file path from showcase section data attribute
                const showcaseSection = document.querySelector('.showcase-section');
                const modelHash = showcaseSection?.dataset.modelHash;
                const modelFilePath = showcaseSection?.dataset.filepath;
                
                if (!modelFilePath) {
                    throw new Error('Could not determine model file path');
                }
                
                // Get the media wrapper and media element
                const mediaWrapper = this.closest('.media-wrapper');
                const mediaElement = mediaWrapper.querySelector('img, video');
                
                if (!mediaElement) {
                    throw new Error('Media element not found');
                }
                
                // Get NSFW level from the wrapper or media element
                const nsfwLevel = parseInt(mediaWrapper.dataset.nsfwLevel || mediaElement.dataset.nsfwLevel || '0', 10);
                
                // Get local file path if available
                const useLocalFile = mediaElement.dataset.localSrc && !mediaElement.dataset.localSrc.includes('undefined');
                const apiClient = getModelApiClient();
                
                if (useLocalFile) {
                    // We have a local file, use it directly
                    const response = await fetch(mediaElement.dataset.localSrc);
                    const blob = await response.blob();
                    const file = new File([blob], 'preview.jpg', { type: blob.type });

                    // Use the existing baseModelApi uploadPreview method with nsfw level
                    await apiClient.uploadPreview(modelFilePath, file, nsfwLevel);
                } else {
                    // Remote file - send URL to backend to download (avoids CORS issues)
                    const imageUrl = mediaElement.src || mediaElement.dataset.remoteSrc;
                    if (!imageUrl) {
                        throw new Error('No image URL available');
                    }

                    // Use the new setPreviewFromUrl method
                    await apiClient.setPreviewFromUrl(modelFilePath, imageUrl, nsfwLevel);
                }
            } catch (error) {
                console.error('Error setting preview:', error);
                showToast('toast.exampleImages.setPreviewFailed', {}, 'error');
            } finally {
                // Restore button state
                this.innerHTML = '<i class="fas fa-image"></i>';
                this.disabled = false;
            }
        });
    });
}

/**
 * Position media controls within the actual rendered media rectangle
 * @param {HTMLElement} mediaWrapper - The wrapper containing the media and controls
 */
export function positionMediaControlsInMediaRect(mediaWrapper) {
    const mediaElement = mediaWrapper.querySelector('img, video');
    const controlsElement = mediaWrapper.querySelector('.media-controls');
    
    if (!mediaElement || !controlsElement) return;
    
    // Get wrapper dimensions
    const wrapperRect = mediaWrapper.getBoundingClientRect();
    
    // Calculate the actual rendered media rectangle
    const mediaRect = getRenderedMediaRect(
        mediaElement, 
        wrapperRect.width, 
        wrapperRect.height
    );
    
    // Calculate the position for controls - place them inside the actual media area
    const padding = 8; // Padding from the edge of the media
    
    // Position at top-right inside the actual media rectangle
    controlsElement.style.top = `${mediaRect.top + padding}px`;
    controlsElement.style.right = `${wrapperRect.width - mediaRect.right + padding}px`;
    
    // Also position any toggle blur buttons in the same way but on the left
    const toggleBlurBtn = mediaWrapper.querySelector('.toggle-blur-btn');
    if (toggleBlurBtn) {
        toggleBlurBtn.style.top = `${mediaRect.top + padding}px`;
        toggleBlurBtn.style.left = `${mediaRect.left + padding}px`;
    }
}

/**
 * Position all media controls in a container
 * @param {HTMLElement} container - Container with media wrappers
 */
export function positionAllMediaControls(container) {
    const mediaWrappers = container.querySelectorAll('.media-wrapper');
    mediaWrappers.forEach(wrapper => {
        positionMediaControlsInMediaRect(wrapper);
    });
}

function applyNsfwLevelChange(mediaWrapper, nsfwLevel) {
    if (!mediaWrapper) return;

    const mediaElement = mediaWrapper.querySelector('img, video');
    if (mediaElement) {
        mediaElement.dataset.nsfwLevel = String(nsfwLevel);
    }
    mediaWrapper.dataset.nsfwLevel = String(nsfwLevel);

    const matureBlurThreshold = getMatureBlurThreshold(state.settings);
    const shouldBlur = state.settings.blur_mature_content && nsfwLevel >= matureBlurThreshold;
    let overlay = mediaWrapper.querySelector('.nsfw-overlay');
    let toggleBtn = mediaWrapper.querySelector('.toggle-blur-btn');

    if (shouldBlur) {
        mediaWrapper.classList.add('nsfw-media-wrapper');
        if (mediaElement) {
            mediaElement.classList.add('blurred');
        }
        if (!overlay) {
            overlay = document.createElement('div');
            overlay.className = 'nsfw-overlay';
            overlay.innerHTML = `
                <div class="nsfw-warning">
                    <p>Mature Content</p>
                    <button class="show-content-btn">Show</button>
                </div>
            `;
            mediaWrapper.appendChild(overlay);
        } else {
            overlay.style.display = 'flex';
        }

        if (!toggleBtn) {
            toggleBtn = document.createElement('button');
            toggleBtn.className = 'toggle-blur-btn showcase-toggle-btn';
            toggleBtn.title = 'Toggle blur';
            toggleBtn.innerHTML = '<i class="fas fa-eye"></i>';
            mediaWrapper.insertBefore(toggleBtn, mediaWrapper.firstChild);
        } else {
            const icon = toggleBtn.querySelector('i');
            if (icon) {
                icon.className = 'fas fa-eye';
            }
        }
    } else {
        mediaWrapper.classList.remove('nsfw-media-wrapper');
        if (mediaElement) {
            mediaElement.classList.remove('blurred');
        }
        if (overlay) {
            overlay.style.display = 'none';
        }
        if (toggleBtn) {
            const icon = toggleBtn.querySelector('i');
            if (icon) {
                icon.className = 'fas fa-eye-slash';
            }
        }
    }

    // Re-bind blur toggles for any newly added elements
    initNsfwBlurHandlers(mediaWrapper);
}

function initSetNsfwHandlers(container) {
    const nsfwButtons = container.querySelectorAll('.set-nsfw-btn');
    const selector = getNsfwLevelSelector();

    nsfwButtons.forEach((btn) => {
        btn.addEventListener('click', (e) => {
            e.stopPropagation();

            if (!selector) {
                console.warn('NSFW selector not available');
                return;
            }

            const mediaWrapper = btn.closest('.media-wrapper');
            const currentLevel = parseInt(mediaWrapper?.dataset.nsfwLevel || '0', 10);
            const modelHash = document.querySelector('.showcase-section')?.dataset.modelHash;
            const mediaSource = btn.dataset.mediaSource || 'civitai';
            const mediaIndex = parseInt(btn.dataset.mediaIndex || '-1', 10);
            const mediaId = btn.dataset.mediaId || '';

            selector.show({
                currentLevel,
                onSelect: async (level) => {
                    if (!modelHash) {
                        showToast('toast.contextMenu.contentRatingFailed', { message: 'Missing model hash' }, 'error');
                        return false;
                    }

                    const originalIcon = btn.innerHTML;
                    btn.disabled = true;
                    btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';

                    try {
                        const payload = {
                            model_hash: modelHash,
                            nsfw_level: level,
                            source: mediaSource,
                        };

                        if (mediaSource === 'custom') {
                            payload.id = mediaId;
                        } else {
                            payload.index = mediaIndex;
                        }

                        const response = await fetch('/api/lm/example-images/set-nsfw-level', {
                            method: 'POST',
                            headers: {
                                'Content-Type': 'application/json',
                            },
                            body: JSON.stringify(payload),
                        });

                        const result = await response.json();
                        if (!result.success) {
                            throw new Error(result.error || 'Failed to update NSFW level');
                        }

                        applyNsfwLevelChange(mediaWrapper, level);
                        showToast('toast.contextMenu.contentRatingSet', { level: getNSFWLevelName(level) }, 'success');
                        return true;
                    } catch (error) {
                        console.error('Error updating NSFW level:', error);
                        showToast('toast.contextMenu.contentRatingFailed', { message: error.message }, 'error');
                        return false;
                    } finally {
                        btn.disabled = false;
                        btn.innerHTML = originalIcon;
                    }
                },
            });
        });
    });
}
