(function() {
    'use strict';
    // Result image fade-in (moved from inline onload for WP.org compliance)
    document.addEventListener('DOMContentLoaded', function() {
        var _ri = document.getElementById('toc-result-img');
        if (_ri) _ri.addEventListener('load', function() {
            this.style.opacity = '1';
            var _l = document.getElementById('toc-result-loading');
            if (_l) _l.style.display = 'none';
        });
    });


    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initTryOnCloud);
    } else {
        initTryOnCloud();
    }

    function initTryOnCloud() {
        // Fix #1: no apiKey or apiUrl in config — proxy handles it server-side
        var config = window.aivtConfig || { ajaxUrl: '', nonce: '', productImageUrl: '', maxTryons: 0, limitPeriod: 'daily' };

        // Fix #8: debug only via URL param, never from config
        var debug = window.location.search.indexOf('aivt_debug=1') !== -1;

        var state = { userFile: null, userDataUrl: null, step: 'upload' };

        // Fix #5: isGenerating lock — prevents double-tap quota waste
        var isGenerating = false;

        var modal     = document.getElementById('toc-modal');
        var backdrop  = document.getElementById('toc-backdrop');
        var openBtn   = document.getElementById('aivt-tryon-btn');
        var closeBtn  = document.getElementById('toc-close-btn');
        var fileInput = document.getElementById('toc-file-input');
        var uploadBtn = document.getElementById('toc-upload-btn');

        if (!modal || !openBtn) return;

        openBtn.addEventListener('click', function(e) { e.preventDefault(); openModal(); });
        closeBtn.addEventListener('click', closeModal);
        backdrop.addEventListener('click', closeModal);
        document.addEventListener('keydown', function(e) { if (e.key === 'Escape') closeModal(); });

        uploadBtn.addEventListener('click', function(e) {
            e.preventDefault(); e.stopPropagation(); fileInput.click();
        });

        fileInput.addEventListener('change', function(e) {
            if (e.target.files && e.target.files.length > 0) handleFile(e.target.files[0]);
        });

        document.getElementById('toc-change-btn').addEventListener('click', function() { resetToUpload(); });

        // Fix #5: generate button — only fires if not already generating
        document.getElementById('toc-generate-btn').addEventListener('click', function() {
            if (isGenerating) return;
            startGeneration(0);
        });

        document.getElementById('toc-view-product-btn').addEventListener('click', function() {
            closeModal();
            var product = document.querySelector('.product, .product-single, [data-product]');
            if (product) product.scrollIntoView({ behavior: 'smooth' });
        });

        document.getElementById('toc-download-btn').addEventListener('click', function() {
            var resultImg = document.getElementById('toc-result-img');
            if (resultImg && resultImg.src) downloadImage(resultImg.src, 'tryon-result-' + Date.now() + '.png');
        });

        document.getElementById('toc-delete-photo-btn').addEventListener('click', function() {
            if (state.userDataUrl) {
                try {
                    var photos = JSON.parse(localStorage.getItem('toc_photos') || '[]');
                    photos = photos.filter(function(p) { return p.url !== state.userDataUrl; });
                    try { localStorage.setItem('toc_photos', JSON.stringify(photos)); } catch(e) {}
                } catch(e) {}
            }
            document.getElementById('toc-result-img').src = '';
            resetToUpload();
        });

        document.getElementById('toc-retry-btn').addEventListener('click', function() { resetToUpload(); });
        document.getElementById('toc-history-btn').addEventListener('click', toggleHistory);
        document.getElementById('toc-history-close').addEventListener('click', closeHistory);

        // Fix #5: centralised lock setter
        function setGenerating(value) {
            isGenerating = value;
            var btn = document.getElementById('toc-generate-btn');
            if (!btn) return;
            btn.disabled = value;
            btn.textContent = value ? 'Generating...' : 'Try It On Now';
        }

        // Hardware-level device fingerprint — survives incognito, VPN, network switches.
        // Uses canvas + WebGL GPU info + screen + fonts + timezone — same technique as
        // used by Stripe, Cloudflare, and major fraud prevention SaaS platforms.
        function getDeviceId() {
            // Return cached value within the same page session
            if (window._tocFp) return window._tocFp;

            var components = [];

            // 1. Canvas fingerprint — GPU rendering is unique per device hardware
            try {
                var canvas = document.createElement('canvas');
                canvas.width = 200; canvas.height = 50;
                var ctx = canvas.getContext('2d');
                ctx.textBaseline = 'alphabetic';
                ctx.fillStyle = '#f60'; ctx.fillRect(125, 1, 62, 20);
                ctx.fillStyle = '#069';
                ctx.font = '11pt Arial';
                ctx.fillText('TryOnCloud\ud83d\udc55', 2, 15);
                ctx.fillStyle = 'rgba(102,204,0,0.7)';
                ctx.font = '18pt Georgia';
                ctx.fillText('TryOnCloud', 4, 45);
                components.push(canvas.toDataURL());
            } catch(e) { components.push('nocanvas'); }

            // 2. WebGL GPU renderer — most unique hardware signal
            try {
                var gl = document.createElement('canvas').getContext('webgl') ||
                         document.createElement('canvas').getContext('experimental-webgl');
                if (gl) {
                    var ext = gl.getExtension('WEBGL_debug_renderer_info');
                    if (ext) {
                        components.push(gl.getParameter(ext.UNMASKED_VENDOR_WEBGL));
                        components.push(gl.getParameter(ext.UNMASKED_RENDERER_WEBGL));
                    }
                    components.push(gl.getParameter(gl.VERSION));
                    components.push(gl.getParameter(gl.SHADING_LANGUAGE_VERSION));
                }
            } catch(e) { components.push('nowebgl'); }

            // 3. Screen metrics
            components.push(screen.width + 'x' + screen.height + 'x' + screen.colorDepth);
            components.push(window.devicePixelRatio || 1);

            // 4. Timezone — consistent per device location
            try { components.push(Intl.DateTimeFormat().resolvedOptions().timeZone); } catch(e) {}

            // 5. Platform + language
            components.push(navigator.platform || '');
            components.push(navigator.language || '');
            components.push(navigator.hardwareConcurrency || 0);
            components.push(navigator.deviceMemory || 0);

            // 6. Font detection — presence of fonts varies by OS/device
            var testFonts = ['Arial','Verdana','Georgia','Courier New','Tahoma','Trebuchet MS','Impact','Comic Sans MS'];
            var testStr = 'mmmmwwww';
            var baseFont = 'monospace';
            var testEl = document.createElement('span');
            testEl.style.cssText = 'position:absolute;left:-9999px;font-size:72px;';
            testEl.innerHTML = testStr;
            document.body.appendChild(testEl);
            var baseFonts = {};
            testEl.style.fontFamily = baseFont;
            baseFonts.w = testEl.offsetWidth; baseFonts.h = testEl.offsetHeight;
            var fontBits = '';
            testFonts.forEach(function(f) {
                testEl.style.fontFamily = "'" + f + "'," + baseFont;
                fontBits += (testEl.offsetWidth !== baseFonts.w || testEl.offsetHeight !== baseFonts.h) ? '1' : '0';
            });
            document.body.removeChild(testEl);
            components.push(fontBits);

            // 7. Touch support
            components.push(navigator.maxTouchPoints || 0);

            // Hash all components into a short fingerprint using FNV-1a
            var str = components.join('~~~');
            var hash = 2166136261;
            for (var i = 0; i < str.length; i++) {
                hash ^= str.charCodeAt(i);
                hash = (hash * 16777619) >>> 0;
            }
            var fp = 'hw_' + hash.toString(16).padStart(8, '0');

            // Persist to localStorage so repeat visits are instant
            try { localStorage.setItem('toc_device_id', fp); } catch(e) {}
            window._tocFp = fp;
            return fp;
        }

        function disableUploadDueToLimit(when) {
            var btn = document.getElementById('toc-upload-btn');
            var inp = document.getElementById('toc-file-input');
            if (btn) {
                btn.disabled = true;
                btn.style.opacity = '0.4';
                btn.style.cursor = 'not-allowed';
                btn.style.pointerEvents = 'none';
                btn.style.boxShadow = 'none';
                var btnSpan = btn.querySelector('span:last-child');
                if (btnSpan) btnSpan.textContent = 'Limit Reached';
            }
            if (inp) inp.disabled = true;
            // Turn limit bar red
            var bar = document.getElementById('toc-limit-bar');
            var barText = document.getElementById('toc-limit-bar-text');
            var barIcon = document.getElementById('toc-limit-bar-icon');
            if (bar && barText) {
                barText.textContent = '0 try-ons left — limit reached';
                bar.style.display = 'flex';
                bar.style.background = '#fef2f2';
                bar.style.borderColor = '#fecaca';
                barText.style.color = '#dc2626';
                if (barIcon) barIcon.setAttribute('stroke', '#dc2626');
            }
            // Show error step with clear reset message
            var periodLabels = { daily: 'tomorrow', weekly: 'next week', monthly: 'next month' };
            var reset = periodLabels[when || config.limitPeriod] || 'later';
            showError('You\'ve used all ' + config.maxTryons + ' try-on(s) for this period. Please try again ' + reset + '.');
        }

        function openModal() {
            if (config.productImageUrl) {
                document.getElementById('toc-product-img').src = config.productImageUrl;
                document.getElementById('toc-product-confirm').src = config.productImageUrl;
                document.getElementById('toc-proc-product').src = config.productImageUrl;
            }
            var termsLink = document.getElementById('toc-terms-link');
            var privacyLink = document.getElementById('toc-privacy-link');
            var confirmTermsLink = document.getElementById('toc-confirm-terms-link');
            var confirmPrivacyLink = document.getElementById('toc-confirm-privacy-link');
            if (config.termsUrl) {
                if (termsLink) termsLink.href = config.termsUrl;
                if (confirmTermsLink) confirmTermsLink.href = config.termsUrl;
            }
            if (config.privacyUrl) {
                if (privacyLink) privacyLink.href = config.privacyUrl;
                if (confirmPrivacyLink) confirmPrivacyLink.href = config.privacyUrl;
            }
            modal.style.display = 'flex';
            modal.style.alignItems = 'center';
            modal.style.justifyContent = 'center';
            modal.style.padding = '16px';
            document.body.style.overflow = 'hidden';
            showStep('upload');
            loadHistory();
            updateHistoryCount();

            // Pre-check limit — show remaining badge, disable upload button if limit hit
            if (config.maxTryons > 0 && config.ajaxUrl && config.nonce) {
                var fd = new FormData();
                fd.append('action', 'aivt_check_limit');
                fd.append('nonce', config.nonce);
                fd.append('device_id', getDeviceId());
                fetch(config.ajaxUrl, { method: 'POST', body: fd, credentials: 'same-origin' })
                    .then(function(r) { return r.json(); })
                    .then(function(data) {
                        if (!data.success) return;
                        var d = data.data;
                        var bar = document.getElementById('toc-limit-bar');
                        var barText = document.getElementById('toc-limit-bar-text');
                        if (bar && barText && d.remaining !== null && d.remaining !== undefined) {
                            var periodWord = { daily: 'today', weekly: 'this week', monthly: 'this month' };
                            var whenLabel = periodWord[config.limitPeriod] || '';
                            if (d.allowed === false || d.remaining === 0) {
                                disableUploadDueToLimit(config.limitPeriod);
                            } else {
                                barText.textContent = d.remaining + ' try-on' + (d.remaining === 1 ? '' : 's') + ' left ' + whenLabel;
                                bar.style.display = 'flex';
                            }
                        }
                    })
                    .catch(function() {}); // silent — server enforces anyway
            }
        }

        function closeModal() {
            modal.style.display = 'none';
            document.body.style.overflow = '';
            closeHistory();
        }

        function showStep(step) {
            state.step = step;
            ['upload','confirm','processing','result','error'].forEach(function(s) {
                var el = document.getElementById('toc-step-' + s);
                if (el) el.style.display = (s === step) ? 'flex' : 'none';
            });
        }

        function normalizeImage(file) {
            return new Promise(function(resolve, reject) {
                var img = new Image();
                img.crossOrigin = 'anonymous';
                var objectUrl = URL.createObjectURL(file);
                img.onload = function() {
                    URL.revokeObjectURL(objectUrl);
                    var MAX_DIM = 2048, w = img.naturalWidth, h = img.naturalHeight;
                    if (w > MAX_DIM || h > MAX_DIM) {
                        if (w >= h) { h = Math.round(h * MAX_DIM / w); w = MAX_DIM; }
                        else        { w = Math.round(w * MAX_DIM / h); h = MAX_DIM; }
                    }
                    var canvas = document.createElement('canvas');
                    canvas.width = w; canvas.height = h;
                    canvas.getContext('2d').drawImage(img, 0, 0, w, h);
                    var quality = 0.85;
                    (function attempt() {
                        canvas.toBlob(function(blob) {
                            if (!blob) return reject(new Error('Failed to compress image.'));
                            if (blob.size > 1.5 * 1024 * 1024 && quality > 0.5) { quality -= 0.1; attempt(); }
                            else resolve(blob);
                        }, 'image/jpeg', quality);
                    })();
                };
                img.onerror = function() { URL.revokeObjectURL(objectUrl); reject(new Error('Could not read image.')); };
                img.src = objectUrl;
            });
        }

        function handleFile(file) {
            // Fix #4: client-side type check (server-side MIME check is in PHP proxy)
            var isHeic = file.name && /\.(heic|heif)$/i.test(file.name);
            var validTypes = ['image/jpeg','image/jpg','image/png','image/webp','image/gif','image/heic','image/heif'];
            if (validTypes.indexOf(file.type) === -1 && !isHeic) {
                showError('Please upload a JPEG, PNG, WebP, or HEIC image.'); return;
            }
            if (file.size > 15 * 1024 * 1024) { showError('Image must be under 15MB.'); return; }
            normalizeImage(file).then(function(normalizedBlob) {
                state.userFile = new File([normalizedBlob], 'photo.jpg', { type: 'image/jpeg' });
                var reader = new FileReader();
                reader.onload = function(e) {
                    state.userDataUrl = e.target.result;
                    document.getElementById('toc-user-preview').src = state.userDataUrl;
                    document.getElementById('toc-proc-user').src = state.userDataUrl;
                    savePhoto(state.userDataUrl);
                    showStep('confirm');
                };
                reader.onerror = function() { showError('Could not read the image.'); };
                reader.readAsDataURL(normalizedBlob);
            }).catch(function(err) { showError(err.message || 'Could not process image.'); });
        }

        function resetToUpload() {
            fileInput.value = '';
            state.userFile = null;
            state.userDataUrl = null;
            setGenerating(false); // Fix #5: release lock
            stopAnimation();
            showStep('upload');
        }

        // Fix #1 + #3: all SaaS calls go through WP AJAX proxy — key never in browser
        function startGeneration(retryCount) {
            retryCount = retryCount || 0;
            var maxRetries = 2;

            if (!state.userFile && !state.userDataUrl) {
                showError('Please select a photo first.'); return;
            }
            if (!config.ajaxUrl || !config.nonce) {
                showError('Virtual try-on is not configured correctly.'); return;
            }

            setGenerating(true); // Fix #5: acquire lock
            showStep('processing');
            animateProgress();

            (async function() {
                try {
                    var fileToUpload = state.userFile;
                    if (!fileToUpload && state.userDataUrl) {
                        var resp = await fetch(state.userDataUrl);
                        var blob = await resp.blob();
                        fileToUpload = await normalizeImage(blob)
                            .then(function(b) { return new File([b], 'photo.jpg', { type: 'image/jpeg' }); })
                            .catch(function() { return blob; });
                    }

                    // Fix #1: POST to WP AJAX proxy — Fix #3: nonce in every request
                    var formData = new FormData();
                    formData.append('action', 'aivt_tryon');
                    formData.append('nonce', config.nonce);
                    formData.append('user_image', fileToUpload, 'photo.jpg');
                    formData.append('product_image_url', config.productImageUrl);
                    formData.append('device_id', getDeviceId());

                    var proxyRes = await fetch(config.ajaxUrl, {
                        method: 'POST', body: formData, credentials: 'same-origin',
                    });

                    var responseData = await proxyRes.json().catch(function() { return {}; });
                    if (debug) console.log('[TryOnCloud] Proxy response:', proxyRes.status, responseData);

                    // Fix #3: nonce expired — friendly message, not a generic error
                    if (proxyRes.status === 403 && responseData.data && responseData.data.code === 'NONCE_EXPIRED') {
                        showError('Session expired. Please refresh the page and try again.');
                        setGenerating(false); return;
                    }

                    if (!responseData.success) {
                        var errMsg  = (responseData.data && responseData.data.message) || 'Something went wrong. Please try again.';
                        var errCode = (responseData.data && responseData.data.code) || '';
                        // Never retry on limit-reached (429) — disable button permanently
                        if (errCode === 'USER_LIMIT_REACHED' || proxyRes.status === 429) {
                            setGenerating(false);
                            disableUploadDueToLimit(config.limitPeriod);
                            return;
                        }
                        var isRetriable = (['GENERATION_FAILED','SERVICE_UNAVAILABLE'].indexOf(errCode) !== -1 || proxyRes.status >= 500);
                        if (isRetriable && retryCount < maxRetries) {
                            setTimeout(function() { startGeneration(retryCount + 1); }, 1500); return;
                        }
                        showError(errMsg); setGenerating(false); return;
                    }

                    var data = responseData.data || {};
                    if (data.jobId) {
                        await pollForResult(data.jobId);
                    } else if (data.resultUrl) {
                        stopAnimation();
                        showResultWithLoading(data.resultUrl);
                        saveResult(data.resultUrl, state.userDataUrl, config.productImageUrl);
                        setGenerating(false);
                    } else {
                        throw new Error('Unexpected response. Please try again.');
                    }

                } catch(err) {
                    var isRetriable = err.name === 'TypeError' || (err.message && err.message.indexOf('fetch') >= 0);
                    if (isRetriable && retryCount < maxRetries) {
                        setTimeout(function() { startGeneration(retryCount + 1); }, 1500); return;
                    }
                    showError(err.message || 'Something went wrong. Please try again.');
                    setGenerating(false);
                }
            })();
        }

        // Fix #9: exponential backoff polling with rotating progress messages
        async function pollForResult(jobId) {
            var maxWait = 90000, interval = 2000, MAX_INTERVAL = 15000, elapsed = 0;
            var messages = ['Processing your try-on...','Analyzing your photo...','Fitting the garment...','Almost there...','Still working...'];
            var msgIndex = 0;

            function nextMessage() {
                var el = document.getElementById('toc-proc-title');
                if (el && msgIndex < messages.length) el.textContent = messages[msgIndex++];
            }
            nextMessage();

            var baseUrl = (config.statusBaseUrl || '').replace(/\/$/, '');
            var statusUrl = baseUrl ? (baseUrl + '/tryon/status/' + jobId) : null;

            while (elapsed < maxWait) {
                await new Promise(function(r) { setTimeout(r, interval); });
                elapsed += interval;
                if (elapsed % 15000 === 0) nextMessage();

                try {
                    var res, data;
                    if (statusUrl) {
                        // No API key required for status endpoint — safe direct call
                        res = await fetch(statusUrl);
                        if (!res.ok) { interval = Math.min(interval * 2, MAX_INTERVAL); continue; }
                        data = await res.json();
                    } else {
                        var fd = new FormData();
                        fd.append('action', 'aivt_tryon_status');
                        fd.append('nonce', config.nonce);
                        fd.append('job_id', jobId);
                        res = await fetch(config.ajaxUrl, { method: 'POST', body: fd, credentials: 'same-origin' });
                        var wrapped = await res.json().catch(function() { return {}; });
                        data = wrapped.data || {};
                    }

                    if (data.status === 'completed' && data.resultUrl) {
                        stopAnimation();
                        showResultWithLoading(data.resultUrl);
                        saveResult(data.resultUrl, state.userDataUrl, config.productImageUrl);
                        setGenerating(false);
                        return;
                    }
                    if (data.status === 'failed') {
                        showError('Generation failed. Please try with a clearer photo.');
                        setGenerating(false); return;
                    }
                    // Exponential backoff: 2s → 4s → 8s → 15s max
                    interval = Math.min(interval * 2, MAX_INTERVAL);
                } catch(e) {
                    if (debug) console.warn('[TryOnCloud] Poll error:', e);
                    interval = Math.min(interval * 2, MAX_INTERVAL);
                }
            }

            // Fix #9: never a dead end — offer retry on timeout
            showError('This is taking longer than usual. Please try again in a moment.');
            setGenerating(false);
        }

        // ── Progress animation ────────────────────────────────────────────────
        var progressInterval = null, tipInterval = null, timerInterval = null, extraTimers = [];

        function animateProgress() {
            stopAnimation();
            var text     = document.getElementById('toc-progress-text');
            var title    = document.getElementById('toc-proc-title');
            var subtitle = document.getElementById('toc-proc-subtitle');
            var tipText  = document.getElementById('toc-tip-text');
            var scanLine = document.getElementById('toc-scan-line');
            var shimmer  = document.getElementById('toc-shimmer');
            var dots     = [document.getElementById('toc-dot-1'), document.getElementById('toc-dot-2'), document.getElementById('toc-dot-3')];
            var segs     = [0,1,2,3,4].map(function(i) { return document.getElementById('toc-seg-' + i); });

            var seconds = 0, step = 0, dotPhase = 0, scanY = 0, scanDir = 1, estimatedTime = 20;
            var stages = [
                { title: 'Analyzing your photo...',   sub: 'Reading body proportions' },
                { title: 'Fitting the garment...',    sub: 'Adjusting for your shape' },
                { title: 'Creating your look...',     sub: 'Mapping fabric details' },
                { title: 'Adding final touches...',   sub: 'Almost ready' },
                { title: 'Finishing up...',           sub: 'Just a moment more' }
            ];
            var tips = ['A head-to-toe shot works best','Natural daylight gives clearer results','Keep arms relaxed at your sides','Plain backgrounds help accuracy','Front-facing poses work great'];
            var currentTip = 0;

            if (text) text.textContent = '~' + estimatedTime + ' sec';
            if (title) title.textContent = stages[0].title;
            if (subtitle) subtitle.textContent = stages[0].sub;
            if (segs[0]) segs[0].style.background = '#059669';

            timerInterval = setInterval(function() {
                if (state.step !== 'processing') { stopAnimation(); return; }
                seconds++;
                if (text) text.textContent = '~' + Math.max(estimatedTime - seconds, 1) + ' sec';
            }, 1000);

            progressInterval = setInterval(function() {
                if (state.step !== 'processing') { stopAnimation(); return; }
                step = Math.min(step + 1, 4);
                for (var i = 0; i <= step; i++) { if (segs[i]) segs[i].style.background = '#059669'; }
                if (stages[step]) {
                    if (title) title.textContent = stages[step].title;
                    if (subtitle) subtitle.textContent = stages[step].sub;
                }
            }, 3500);

            tipInterval = setInterval(function() {
                if (state.step !== 'processing') { stopAnimation(); return; }
                currentTip = (currentTip + 1) % tips.length;
                if (tipText) tipText.textContent = tips[currentTip];
            }, 4000);

            var dotTimer = setInterval(function() {
                if (state.step !== 'processing') return;
                dotPhase = (dotPhase + 1) % 6;
                dots.forEach(function(d, i) {
                    if (!d) return;
                    var active = (dotPhase % 3) === i;
                    d.style.opacity = active ? '1' : '0.25';
                    d.style.transform = active ? 'scale(1.4)' : 'scale(1)';
                    d.style.background = active ? '#FF6A00' : '#fdba74';
                });
            }, 220);
            extraTimers.push(dotTimer);

            var scanTimer = setInterval(function() {
                if (state.step !== 'processing') return;
                scanY += scanDir * 4;
                if (scanY >= 126) { scanY = 126; scanDir = -1; }
                if (scanY <= 0)   { scanY = 0; scanDir = 1; }
                if (scanLine) scanLine.style.top = scanY + 'px';
            }, 30);
            extraTimers.push(scanTimer);

            var shimX = -100;
            var shimTimer = setInterval(function() {
                if (state.step !== 'processing') return;
                shimX += 3; if (shimX > 200) shimX = -100;
                if (shimmer) shimmer.style.background = 'linear-gradient(105deg,transparent ' + (shimX-20) + '%,rgba(255,255,255,0.5) ' + shimX + '%,transparent ' + (shimX+20) + '%)';
            }, 30);
            extraTimers.push(shimTimer);
        }

        function stopAnimation() {
            if (progressInterval) { clearInterval(progressInterval); progressInterval = null; }
            if (timerInterval)    { clearInterval(timerInterval);    timerInterval    = null; }
            if (tipInterval)      { clearInterval(tipInterval);      tipInterval      = null; }
            extraTimers.forEach(function(t) { clearInterval(t); });
            extraTimers = [];
        }

        function showError(msg) {
            stopAnimation();
            document.getElementById('toc-error-msg').textContent = msg;
            showStep('error');
        }

        // Show result step with loading spinner until image loads
        function showResultWithLoading(resultUrl) {
            var img = document.getElementById('toc-result-img');
            var loader = document.getElementById('toc-result-loading');
            // Reset state: show spinner, hide image
            if (loader) loader.style.display = 'flex';
            if (img) { img.style.opacity = '0'; img.src = ''; }
            // Show result step first (with spinner visible)
            showStep('result');
            // Then set the image src to start loading
            if (img) img.src = resultUrl;
        }

        // ── localStorage with 24h TTL + silent try/catch (Fix #7) ────────────
        var PHOTOS_KEY  = 'toc_photos';
        var RESULTS_KEY = 'toc_results';
        var MAX_ITEMS   = 2;
        var TTL         = 24 * 60 * 60 * 1000;

        function getStoredItems(key) {
            try {
                var items = JSON.parse(localStorage.getItem(key) || '[]');
                var now   = Date.now();
                var fresh = items.filter(function(item) { return item.ts && (now - item.ts) < TTL; });
                if (fresh.length !== items.length) {
                    try { localStorage.setItem(key, JSON.stringify(fresh)); } catch(e) {}
                }
                return fresh;
            } catch(e) { return []; }
        }

        function savePhoto(dataUrl) {
            try {
                var photos = getStoredItems(PHOTOS_KEY);
                photos = photos.filter(function(p) { return p.url !== dataUrl; });
                photos.unshift({ url: dataUrl, ts: Date.now() });
                photos = photos.slice(0, MAX_ITEMS);
                try { localStorage.setItem(PHOTOS_KEY, JSON.stringify(photos)); } catch(e) {}
            } catch(e) {}
        }

        function saveResult(resultUrl, userUrl, productImageUrl) {
            try {
                var results = getStoredItems(RESULTS_KEY);
                results = results.filter(function(r) { return r.resultUrl !== resultUrl; });
                results.unshift({ resultUrl: resultUrl, userUrl: userUrl, productImageUrl: productImageUrl, productPageUrl: window.location.href, ts: Date.now() });
                results = results.slice(0, MAX_ITEMS);
                try { localStorage.setItem(RESULTS_KEY, JSON.stringify(results)); } catch(e) {}
            } catch(e) {}
        }

        function loadHistory() {
            var photosList  = document.getElementById('toc-history-photos');
            var resultsList = document.getElementById('toc-history-results');

            // Photos
            try {
                var photos = getStoredItems(PHOTOS_KEY);
                if (photos.length === 0) {
                    photosList.innerHTML = '<p style="font-size:12px;color:#9ca3af;text-align:center;padding:16px 0;">No photos yet</p>';
                } else {
                    var html = '';
                    photos.forEach(function(item, i) {
                        html += '<div style="display:flex;align-items:center;gap:8px;padding:8px;border-radius:6px;margin-bottom:6px;border:1px solid #e5e7eb;background:#fafafa;">'
                            + '<img src="' + item.url + '" style="width:40px;height:48px;object-fit:cover;border-radius:4px;border:1px solid #e5e7eb;" />'
                            + '<button type="button" data-photo-index="' + i + '" style="padding:4px 10px;font-size:11px;border-radius:6px;background:#f5f5f5;border:1px solid #e5e7eb;color:#374151;cursor:pointer;font-weight:500;">Use Photo</button>'
                            + '<button type="button" data-delete-photo="' + i + '" style="width:24px;height:24px;margin-left:auto;display:flex;align-items:center;justify-content:center;background:#fef2f2;border:1px solid #fecaca;border-radius:4px;cursor:pointer;color:#dc2626;font-size:12px;">&times;</button>'
                            + '</div>';
                    });
                    photosList.innerHTML = html;
                    photosList.querySelectorAll('[data-photo-index]').forEach(function(el) {
                        el.addEventListener('click', function() { usePhoto(parseInt(el.getAttribute('data-photo-index'))); });
                    });
                    photosList.querySelectorAll('[data-delete-photo]').forEach(function(btn) {
                        btn.addEventListener('click', function(e) { e.stopPropagation(); deletePhoto(parseInt(btn.getAttribute('data-delete-photo'))); });
                    });
                }
            } catch(e) {
                photosList.innerHTML = '<p style="font-size:12px;color:#9ca3af;text-align:center;padding:16px 0;">No photos yet</p>';
            }

            // Results
            try {
                var results = getStoredItems(RESULTS_KEY);
                if (results.length === 0) {
                    resultsList.innerHTML = '<p style="font-size:12px;color:#9ca3af;text-align:center;padding:16px 0;">No try-ons yet</p>';
                } else {
                    var html = '';
                    results.forEach(function(item, i) {
                        html += '<div style="padding:8px;border-radius:6px;margin-bottom:6px;border:1px solid #e5e7eb;background:#fafafa;">'
                            + '<div style="display:flex;align-items:center;gap:8px;cursor:pointer;margin-bottom:6px;" data-result-index="' + i + '">'
                            + '<img src="' + item.resultUrl + '" style="width:40px;height:48px;object-fit:cover;border-radius:4px;border:2px solid #10b981;" />'
                            + '<span style="flex:1;font-size:11px;color:#374151;">View result</span></div>'
                            + '<div style="display:flex;gap:4px;">'
                            + '<button type="button" data-download-result="' + i + '" style="height:28px;padding:0 8px;border:1px solid #e5e7eb;border-radius:6px;background:#fff;color:#374151;font-size:10px;cursor:pointer;">Save</button>'
                            + '<button type="button" data-buy-result="' + i + '" style="flex:1;height:28px;padding:0 10px;border:none;border-radius:6px;background:#FF6A00;color:#fff;font-size:11px;font-weight:600;cursor:pointer;">Buy</button>'
                            + '<button type="button" data-delete-result="' + i + '" style="width:28px;height:28px;border:1px solid #fecaca;border-radius:6px;background:#fef2f2;color:#dc2626;font-size:11px;cursor:pointer;">&times;</button>'
                            + '</div></div>';
                    });
                    resultsList.innerHTML = html;
                    resultsList.querySelectorAll('[data-result-index]').forEach(function(el) {
                        el.addEventListener('click', function() { viewResult(parseInt(el.getAttribute('data-result-index'))); });
                    });
                    resultsList.querySelectorAll('[data-download-result]').forEach(function(btn) {
                        btn.addEventListener('click', function(e) { e.stopPropagation(); downloadResult(parseInt(btn.getAttribute('data-download-result'))); });
                    });
                    resultsList.querySelectorAll('[data-buy-result]').forEach(function(btn) {
                        btn.addEventListener('click', function(e) { e.stopPropagation(); buyFromResult(parseInt(btn.getAttribute('data-buy-result'))); });
                    });
                    resultsList.querySelectorAll('[data-delete-result]').forEach(function(btn) {
                        btn.addEventListener('click', function(e) { e.stopPropagation(); deleteResult(parseInt(btn.getAttribute('data-delete-result'))); });
                    });
                }
            } catch(e) {
                resultsList.innerHTML = '<p style="font-size:12px;color:#9ca3af;text-align:center;padding:16px 0;">No try-ons yet</p>';
            }
        }

        function usePhoto(index) {
            try {
                var photos = getStoredItems(PHOTOS_KEY);
                if (photos[index]) {
                    state.userDataUrl = photos[index].url;
                    state.userFile = null;
                    document.getElementById('toc-user-preview').src = state.userDataUrl;
                    document.getElementById('toc-proc-user').src = state.userDataUrl;
                    closeHistory();
                    showStep('confirm');
                }
            } catch(e) {}
        }

        function viewResult(index) {
            try {
                var results = getStoredItems(RESULTS_KEY);
                if (results[index]) {
                    document.getElementById('toc-result-img').src = results[index].resultUrl;
                    closeHistory();
                    showStep('result');
                }
            } catch(e) {}
        }

        function deletePhoto(index) {
            try {
                var photos = getStoredItems(PHOTOS_KEY);
                photos.splice(index, 1);
                try { localStorage.setItem(PHOTOS_KEY, JSON.stringify(photos)); } catch(e) {}
                loadHistory();
            } catch(e) {}
        }

        function deleteResult(index) {
            try {
                var results = getStoredItems(RESULTS_KEY);
                results.splice(index, 1);
                try { localStorage.setItem(RESULTS_KEY, JSON.stringify(results)); } catch(e) {}
                loadHistory();
                updateHistoryCount();
            } catch(e) {}
        }

        function downloadResult(index) {
            try {
                var results = getStoredItems(RESULTS_KEY);
                if (results[index]) downloadImage(results[index].resultUrl, 'tryon-result.png');
            } catch(e) {}
        }

        function buyFromResult(index) {
            try {
                var results = getStoredItems(RESULTS_KEY);
                if (results[index] && results[index].productPageUrl) window.location.href = results[index].productPageUrl;
            } catch(e) {}
        }

        function downloadImage(url, filename) {
            // Append ?download=1 so server sends Content-Disposition: attachment
            var dlUrl = url + (url.indexOf('?') === -1 ? '?' : '&') + 'download=1';
            var link = document.createElement('a');
            link.href = dlUrl;
            link.download = filename || 'tryon-result.png';
            link.target = '_blank';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }

        function updateHistoryCount() {
            try {
                var results = getStoredItems(RESULTS_KEY);
                var countEl = document.getElementById('toc-history-count');
                if (countEl) countEl.textContent = results.length + ' Try-On' + (results.length !== 1 ? 's' : '');
            } catch(e) {}
        }

        function toggleHistory() {
            var panel = document.getElementById('toc-history-panel');
            panel.style.display = panel.style.display === 'flex' ? 'none' : 'flex';
        }

        function closeHistory() {
            document.getElementById('toc-history-panel').style.display = 'none';
        }
    }
})();
