(function () {
    'use strict';

    const config = window.h4bsImageStudio || {};
    const root = document.querySelector('[data-h4bs-image-studio]');
    if (!root) return;

    const find = (selector) => root.querySelector(selector);
    const canvas = find('[data-h4bs-canvas]');
    const ctx = canvas ? canvas.getContext('2d') : null;
    if (!canvas || !ctx) return;

    const start = find('[data-h4bs-start]');
    const workspace = find('[data-h4bs-workspace]');
    const pickButtons = root.querySelectorAll('[data-h4bs-pick], [data-h4bs-replace]');
    const toolButtons = root.querySelectorAll('[data-h4bs-tool]');
    const cropApplyButton = find('[data-h4bs-crop-apply]');
    const overlayApplyButton = find('[data-h4bs-overlay-apply]');
    const undoButton = find('[data-h4bs-undo]');
    const redoButton = find('[data-h4bs-redo]');
    const resetButton = find('[data-h4bs-reset]');
    const saveButton = find('[data-h4bs-save]');
    const savedLink = find('[data-h4bs-saved-link]');
    const statusLine = find('[data-h4bs-status]');
    const originalTitle = find('[data-h4bs-original-title]');
    const copyState = find('[data-h4bs-copy-state]');
    const drawColorInput = find('[data-h4bs-draw-color]');
    const brushSizeInput = find('[data-h4bs-brush-size]');
    const overlayColorInput = find('[data-h4bs-overlay-color]');
    const overlayOpacityInput = find('[data-h4bs-overlay-opacity]');
    const altInput = find('[data-h4bs-alt]');
    const altWrap = find('[data-h4bs-alt-wrap]');
    const altModes = root.querySelectorAll('[data-h4bs-alt-mode]');

    const state = {
        attachmentId: Number((config.initial && config.initial.id) || 0),
        originalUrl: String((config.initial && config.initial.url) || ''),
        sourceMime: String((config.initial && config.initial.mime) || ''),
        originalSnapshot: '', savedSnapshot: '',
        mode: '', drawing: false, cropStart: null, cropRect: null, cropSnapshot: null,
        undo: [], redo: [], dirty: false, saving: false, savedCopy: false, requestId: '', activePointerId: null,
    };

    function setStatus(message, kind) {
        if (!statusLine) return;
        statusLine.textContent = message;
        statusLine.dataset.state = kind || 'info';
    }

    function uuid() {
        if (window.crypto && typeof window.crypto.randomUUID === 'function') return window.crypto.randomUUID();
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (character) => {
            const value = Math.random() * 16 | 0;
            return (character === 'x' ? value : (value & 0x3 | 0x8)).toString(16);
        });
    }

    function setDirty(dirty, message) {
        state.dirty = Boolean(dirty);
        root.dataset.dirty = state.dirty ? 'true' : 'false';
        if (copyState) copyState.textContent = state.dirty ? 'Unsaved edits' : (state.savedCopy ? 'New copy saved' : 'Matches the original');
        if (saveButton instanceof HTMLButtonElement) saveButton.disabled = !state.dirty || state.saving;
        if (resetButton instanceof HTMLButtonElement) resetButton.disabled = !state.dirty || state.saving;
        if (state.dirty && message) setStatus(message, 'unsaved');
    }

    function updateHistory() {
        if (undoButton instanceof HTMLButtonElement) undoButton.disabled = state.undo.length === 0 || state.saving;
        if (redoButton instanceof HTMLButtonElement) redoButton.disabled = state.redo.length === 0 || state.saving;
    }

    function canvasSnapshot(mime, quality) { return canvas.toDataURL(mime || 'image/png', quality); }

    function snapshotBytes(snapshot) {
        const comma = snapshot.indexOf(',');
        return Math.ceil(Math.max(0, snapshot.length - comma - 1) * 0.75);
    }

    function historyBytes() {
        return state.undo.concat(state.redo).reduce((total, snapshot) => total + snapshotBytes(snapshot), 0);
    }

    function pushHistory(stack, snapshot) {
        stack.push(snapshot);
        const maxEntries = Math.max(1, Number(config.historyEntries || 10));
        const maxBytes = Math.max(1024 * 1024, Number(config.historyBytes || 32 * 1024 * 1024));
        while (stack.length > maxEntries) stack.shift();
        while (historyBytes() > maxBytes && (state.undo.length + state.redo.length) > 1) {
            if (state.undo.length > 1) state.undo.shift();
            else state.redo.shift();
        }
    }

    function outputMime() {
        return ['image/jpeg', 'image/png', 'image/webp'].includes(state.sourceMime) ? state.sourceMime : 'image/png';
    }

    function restoreSnapshot(dataUrl, callback) {
        const image = new Image();
        image.onload = () => {
            canvas.width = image.naturalWidth;
            canvas.height = image.naturalHeight;
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.drawImage(image, 0, 0);
            state.cropRect = null;
            state.cropStart = null;
            state.cropSnapshot = null;
            if (callback) callback();
        };
        image.onerror = () => setStatus('Could not restore that edit. Your current work is still available.', 'error');
        image.src = dataUrl;
    }

    function remember() {
        if (!state.attachmentId || canvas.width === 0) return;
        state.redo = [];
        pushHistory(state.undo, canvasSnapshot());
        state.requestId = '';
        updateHistory();
    }

    function enableEditing() {
        toolButtons.forEach((button) => { button.disabled = false; });
        if (overlayApplyButton) overlayApplyButton.disabled = false;
        root.dataset.ready = 'true';
    }

    function loadImage(url, title, alt, mime) {
        const image = new Image();
        image.onload = function () {
            if ((image.naturalWidth * image.naturalHeight) > Number(config.maxPixels || 32 * 1024 * 1024)) {
                setStatus('This image is too large for safe browser editing. Choose a smaller original or resize it in Media Library first.', 'error');
                return;
            }
            canvas.width = image.naturalWidth;
            canvas.height = image.naturalHeight;
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.drawImage(image, 0, 0);
            state.originalUrl = url;
            state.sourceMime = String(mime || '');
            state.originalSnapshot = canvasSnapshot();
            state.savedSnapshot = state.originalSnapshot;
            state.undo = [];
            state.redo = [];
            state.requestId = '';
            state.savedCopy = false;
            if (start) start.hidden = true;
            if (workspace) workspace.hidden = false;
            if (originalTitle) originalTitle.textContent = `${title || 'Selected image'} · ${canvas.width} × ${canvas.height}`;
            if (altInput instanceof HTMLInputElement) altInput.value = String(alt || '');
            setDirty(false);
            updateHistory();
            enableEditing();
            setStatus('Original loaded. It will not be changed.', 'saved');
            canvas.focus();
        };
        image.onerror = () => setStatus('Could not load this image. Choose another Media Library image.', 'error');
        image.src = url;
    }

    function openMediaPicker(event) {
        if (!window.wp || !window.wp.media) {
            setStatus('WordPress Media Library is unavailable. Reload and try again.', 'error');
            return;
        }
        if (state.dirty && !window.confirm('Discard these unsaved image edits and choose a different original?')) return;
        const opener = event && event.currentTarget;
        const frame = window.wp.media({title: 'Choose original image', library: {type: 'image'}, multiple: false, button: {text: 'Use this image'}});
        frame.on('select', function () {
            const first = frame.state().get('selection').first();
            const data = first ? first.toJSON() : null;
            if (!data) return;
            const sourceUrl = data.url || (data.sizes && data.sizes.full && data.sizes.full.url) || '';
            if (!sourceUrl) return setStatus('Selected media has no usable full-size image.', 'error');
            state.attachmentId = Number(data.id || 0);
            loadImage(sourceUrl, data.title || data.filename, data.alt || '', data.mime || '');
        });
        frame.on('close', () => { if (opener && opener.isConnected) opener.focus(); });
        frame.open();
    }

    function toCanvasPoint(event) {
        const rect = canvas.getBoundingClientRect();
        return {x: (event.clientX - rect.left) * canvas.width / rect.width, y: (event.clientY - rect.top) * canvas.height / rect.height};
    }
    function normalizeRect(a, b) { return {x: Math.min(a.x,b.x), y: Math.min(a.y,b.y), w: Math.abs(b.x-a.x), h: Math.abs(b.y-a.y)}; }
    function drawCropOutline(rect) {
        ctx.save(); ctx.strokeStyle='#0a9b7a'; ctx.lineWidth=2; ctx.setLineDash([8,4]); ctx.strokeRect(rect.x,rect.y,rect.w,rect.h); ctx.restore();
    }
    function setMode(mode) {
        state.mode = mode;
        toolButtons.forEach((button) => button.setAttribute('aria-pressed', button.dataset.h4bsTool === mode ? 'true' : 'false'));
        setStatus(mode === 'crop' ? 'Crop mode. Drag the area to keep.' : 'Draw mode. Draw directly on the working copy.', 'info');
    }
    function rgbaFromHex(hex, opacity) {
        const raw=String(hex||'').replace('#','').padEnd(6,'0').slice(0,6);
        return `rgba(${parseInt(raw.slice(0,2),16)}, ${parseInt(raw.slice(2,4),16)}, ${parseInt(raw.slice(4,6),16)}, ${Math.max(0,Math.min(1,Number(opacity)/100))})`;
    }
    function applyCrop() {
        if (!state.cropRect || state.cropRect.w < 2 || state.cropRect.h < 2) return setStatus('Drag a crop area first.', 'error');
        const before = state.cropSnapshot ? (() => { const temp=document.createElement('canvas'); temp.width=canvas.width; temp.height=canvas.height; temp.getContext('2d').putImageData(state.cropSnapshot,0,0); return temp.toDataURL('image/png'); })() : canvasSnapshot();
        state.redo=[]; pushHistory(state.undo, before);
        const crop=state.cropRect; const pixels=ctx.getImageData(crop.x,crop.y,crop.w,crop.h);
        canvas.width=Math.max(1,Math.round(crop.w)); canvas.height=Math.max(1,Math.round(crop.h)); ctx.putImageData(pixels,0,0);
        state.cropRect=null; state.cropStart=null; state.cropSnapshot=null; state.requestId=''; updateHistory(); setDirty(true,'Crop applied. Undo is available.');
    }
    function applyOverlay() {
        remember(); ctx.save(); ctx.fillStyle=rgbaFromHex(overlayColorInput ? overlayColorInput.value : '#000000', overlayOpacityInput ? overlayOpacityInput.value : 25); ctx.fillRect(0,0,canvas.width,canvas.height); ctx.restore(); setDirty(true,'Overlay applied. Undo is available.');
    }
    function undo() {
        if (!state.undo.length) return;
        const target=state.undo.pop(); pushHistory(state.redo, canvasSnapshot()); state.requestId='';
        restoreSnapshot(target,()=>{ updateHistory(); setDirty(target !== state.savedSnapshot, state.undo.length ? 'Edit undone.' : 'All edits undone.'); undoButton.focus(); });
    }
    function redo() {
        if (!state.redo.length) return;
        const target=state.redo.pop(); pushHistory(state.undo, canvasSnapshot()); state.requestId='';
        restoreSnapshot(target,()=>{ updateHistory(); setDirty(target !== state.savedSnapshot,'Edit restored.'); redoButton.focus(); });
    }
    function reset() {
        if (!state.dirty || !window.confirm('Reset the working copy to the unchanged original?')) return;
        const beforeReset = canvasSnapshot();
        restoreSnapshot(state.originalSnapshot,()=>{ state.undo=[]; state.redo=[]; pushHistory(state.undo, beforeReset); state.requestId=''; state.savedCopy=false; updateHistory(); setDirty(false); setStatus('Working copy reset to the original. Undo is available.', 'saved'); resetButton.focus(); });
    }
    function updateAltMode() {
        const decorative = Boolean(find('[data-h4bs-alt-mode][value="decorative"]:checked'));
        if (altWrap) altWrap.hidden = decorative;
        if (altInput) altInput.required = !decorative;
    }
    async function saveImage() {
        if (!state.attachmentId || !state.dirty || state.saving) return;
        const decorative=Boolean(find('[data-h4bs-alt-mode][value="decorative"]:checked'));
        const alt=altInput ? altInput.value.trim() : '';
        if (!decorative && !alt) { setStatus('Add an image description or choose decorative.', 'error'); altInput.focus(); return; }
        const output = canvasSnapshot(outputMime(), outputMime() === 'image/png' ? undefined : 0.9);
        if (snapshotBytes(output) > Number(config.maxBytes || 20 * 1024 * 1024)) {
            setStatus('The edited image is too large to upload safely. Crop or resize it before saving.', 'error');
            return;
        }
        state.saving=true; if (!state.requestId) state.requestId=uuid(); updateHistory(); saveButton.disabled=true; setStatus('Saving a new Media Library image…','saving');
        try {
            const response=await fetch(String(config.restRoot||'').replace(/\/+$/,'')+'/'+state.attachmentId+'/editor-save',{method:'POST',headers:{'Content-Type':'application/json','X-WP-Nonce':config.nonce||'','X-H4BS-Request-ID':state.requestId},body:JSON.stringify({image_data:output,alt_text:alt,decorative})});
            const json=await response.json(); if(!response.ok) throw new Error(json && json.message ? json.message : 'Save failed');
            state.savedSnapshot=canvasSnapshot(); state.undo=[]; state.redo=[]; state.requestId=''; state.savedCopy=true; setDirty(false); updateHistory();
            if(savedLink){savedLink.href=String(json.edit_url||'');savedLink.hidden=!json.edit_url;}
            setStatus('New image saved. The original is unchanged.','saved');
        } catch(error) {
            setStatus(`${error && error.message ? error.message : 'Save failed.'} Your edits are still here; try Save again.`, 'error');
        } finally { state.saving=false; updateHistory(); if(saveButton) saveButton.disabled=!state.dirty; }
    }

    canvas.tabIndex=0;
    canvas.addEventListener('pointerdown',(event)=>{
        if(!state.attachmentId||!['draw','crop'].includes(state.mode))return;
        event.preventDefault();
        state.activePointerId=event.pointerId;
        if(canvas.setPointerCapture){
            try{canvas.setPointerCapture(event.pointerId);}catch(error){}
        }
        const point=toCanvasPoint(event);
        if(state.mode==='crop'){state.cropStart=point;state.cropSnapshot=ctx.getImageData(0,0,canvas.width,canvas.height);state.cropRect=null;return;}
        remember(); state.drawing=true;ctx.beginPath();ctx.moveTo(point.x,point.y);
    });
    canvas.addEventListener('pointermove',(event)=>{
        if(!state.attachmentId||event.pointerId!==state.activePointerId)return;
        event.preventDefault();
        const point=toCanvasPoint(event);
        if(state.mode==='crop'&&state.cropStart&&state.cropSnapshot){ctx.putImageData(state.cropSnapshot,0,0);state.cropRect=normalizeRect(state.cropStart,point);drawCropOutline(state.cropRect);return;}
        if(!state.drawing)return;ctx.strokeStyle=drawColorInput?drawColorInput.value:'#ff4d4d';ctx.lineWidth=Number(brushSizeInput?brushSizeInput.value:8);ctx.lineCap='round';ctx.lineJoin='round';ctx.lineTo(point.x,point.y);ctx.stroke();setDirty(true,'Drawing changed. Undo is available.');
    });
    const finish=(event)=>{
        if(event&&state.activePointerId!==null&&event.pointerId!==state.activePointerId)return;
        state.drawing=false;
        if(event&&canvas.releasePointerCapture&&canvas.hasPointerCapture){
            try{
                if(canvas.hasPointerCapture(event.pointerId))canvas.releasePointerCapture(event.pointerId);
            }catch(error){}
        }
        state.activePointerId=null;
        if(state.mode==='crop'&&state.cropRect){if(cropApplyButton)cropApplyButton.disabled=false;setStatus('Crop area ready. Apply it or choose another area.','info');}
    };
    canvas.addEventListener('pointerup',finish);canvas.addEventListener('pointercancel',finish);canvas.addEventListener('lostpointercapture',finish);
    pickButtons.forEach((button)=>button.addEventListener('click',openMediaPicker));
    toolButtons.forEach((button)=>button.addEventListener('click',()=>setMode(button.dataset.h4bsTool||'draw')));
    if(cropApplyButton)cropApplyButton.addEventListener('click',applyCrop);
    if(overlayApplyButton)overlayApplyButton.addEventListener('click',applyOverlay);
    if(undoButton)undoButton.addEventListener('click',undo);
    if(redoButton)redoButton.addEventListener('click',redo);
    if(resetButton)resetButton.addEventListener('click',reset);
    if(saveButton)saveButton.addEventListener('click',saveImage);
    altModes.forEach((radio)=>radio.addEventListener('change',updateAltMode));
    window.addEventListener('beforeunload',(event)=>{if(!state.dirty)return;event.preventDefault();event.returnValue='';});
    updateAltMode(); updateHistory(); setStatus('Choose an image to begin.','info');
    if(state.attachmentId&&state.originalUrl)loadImage(state.originalUrl,(config.initial&&config.initial.title)||'Selected image',(config.initial&&config.initial.alt)||'',(config.initial&&config.initial.mime)||'');
})();
