(function () {
    "use strict";

    const config = window.h4bsImageGuidanceData || {};
    const profiles = config.profiles && typeof config.profiles === "object" ? config.profiles : {};
    const labels = Object.assign({
        title: "Best image size",
        purpose: "Image use",
        recommended: "Recommended",
        formats: "Best formats",
        weight: "Target file weight",
        selected: "Selected image",
        none: "No image selected yet.",
        checking: "Checking image dimensions...",
        good: "Good fit for this use.",
        small: "This image is smaller than the recommended source size and may look soft on larger screens.",
        ratio: "This image uses a different aspect ratio, so review its crop at every device size.",
        format: "This file format is not preferred for this use.",
        mixed: "The selected gallery uses mixed aspect ratios.",
        unavailable: "Dimensions could not be read. Verify the pixel size in Media Library before publishing.",
        openGuide: "Open the complete image guide",
    }, config.labels || {});
    const enhanced = new WeakSet();
    const pending = new WeakMap();
    let scanQueued = false;

    function clean(value) {
        return String(value || "").replace(/\s+/g, " ").trim();
    }

    function identifier(element) {
        const container = element.closest("[data-h4bs-field-key], .h4bs-slider-field, label, td, p, .h4bs-media-control") || element;
        return [
            element.getAttribute("name"),
            element.getAttribute("id"),
            element.getAttribute("data-h4bs-field-key"),
            element.getAttribute("data-media-target"),
            element.getAttribute("bind-slide"),
            element.getAttribute("bind-layer"),
            element.getAttribute("placeholder"),
            container.getAttribute("data-h4bs-field-key"),
            clean(container.textContent).slice(0, 180),
        ].filter(Boolean).join(" ").toLowerCase();
    }

    function explicitProfile(element) {
        const owner = element.closest("[data-h4bs-image-profile]");
        return owner ? clean(owner.getAttribute("data-h4bs-image-profile")) : "";
    }

    function inferProfile(element) {
        const explicit = explicitProfile(element);
        if (explicit && profiles[explicit]) return explicit;
        const key = identifier(element);
        if (/favicon|site[_ -]?icon/.test(key)) return "site_icon";
        if (/social|open graph|og[_ -]?image|twitter[_ -]?image/.test(key)) return "social";
        if (/portrait|headshot|staff photo|team photo/.test(key)) return "portrait";
        if (/video poster|poster image|failure image|fallback image/.test(key)) return "video_poster";
        if (/slider|slide background/.test(key)) return "slider";
        if (/hero|banner|masthead/.test(key)) return "hero";
        if (/background image/.test(key)) return "background";
        if (/logo|wordmark/.test(key)) return "logo";
        if (/product|variant|catalog image/.test(key)) return "product";
        if (/event|program artwork|audition/.test(key)) return "event";
        if (/email|campaign image|newsletter image/.test(key)) return "email";
        if (/screenshot|site shot|website snapshot|portfolio image/.test(key)) return "screenshot";
        if (/gallery|listing image urls|images selected/.test(key)) return "gallery";
        return profiles[config.defaultProfile] ? config.defaultProfile : Object.keys(profiles)[0] || "";
    }

    function isImageControl(element) {
        if (!(element instanceof HTMLElement)) return false;
        const hasExplicitProfile = !!element.closest("[data-h4bs-image-profile]");
        if (!document.body.classList.contains("h4bs-admin-ui") && !hasExplicitProfile) return false;
        if (element.matches("[data-h4bs-image-profile], [data-h4bs-media-input], [data-event-image], [data-select-portrait]")) return true;
        if (element.matches("[data-media-target]") && /image|poster/.test(identifier(element))) return true;
        if (!element.matches("input, textarea")) return false;
        const type = String(element.getAttribute("type") || "text").toLowerCase();
        if (["checkbox", "radio", "submit", "button", "color", "range"].includes(type)) return false;
        const key = identifier(element);
        if (/alt(ernative)? text|max.image.preview|image size|image width|image height|image fit|download.images|image description/.test(key)) return false;
        return /(image|logo|poster|portrait|headshot|photo|gallery|thumbnail|favicon|site[_ -]?icon)/.test(key);
    }

    function controlOwner(element) {
        return element.closest("[data-h4bs-image-profile], [data-h4bs-field-key], .h4bs-slider-field, .h4bs-calendar-image, .h4bs-staff-editor-section, .h4bs-media-control, td, label, p") || element.parentElement;
    }

    function valueInputs(owner, source) {
        const controls = Array.from(owner.querySelectorAll("input, textarea")).filter(isImageControl);
        if (source.matches("input, textarea") && !controls.includes(source)) controls.unshift(source);
        return controls;
    }

    function urlsFrom(owner, source) {
        const values = valueInputs(owner, source).map(function (input) {
            return clean(input.value);
        }).filter(Boolean);
        const previewUrls = Array.from(owner.querySelectorAll("img")).map(function (image) {
            return clean(image.currentSrc || image.src);
        }).filter(Boolean);
        return Array.from(new Set(values.concat(previewUrls).flatMap(function (value) {
            if (/^\s*\[/.test(value)) return [];
            if (/^[1-9][0-9]*$/.test(value)) return ["attachment:" + value];
            const parts = /^data:/i.test(value) ? [value] : value.split(/\s*(?:,|\r?\n)\s*/);
            return parts.filter(function (part) {
                return /^(https?:|data:|blob:|\/)/i.test(part);
            });
        }))).slice(0, 12);
    }

    function extension(url) {
        const match = String(url || "").split(/[?#]/)[0].match(/\.([a-z0-9]+)$/i);
        return match ? match[1].toLowerCase() : "";
    }

    function profileSummary(profile) {
        if (!profile) return "";
        if (Number(profile.width || 0) > 0 && Number(profile.height || 0) > 0) {
            return profile.width + " x " + profile.height + " px · " + profile.ratio;
        }
        return "At least " + profile.width + " px wide · " + profile.ratio;
    }

    function fact(term, value) {
        const wrap = document.createElement("div");
        wrap.className = "h4bs-image-guidance__fact";
        const dt = document.createElement("dt");
        dt.textContent = term;
        const dd = document.createElement("dd");
        dd.textContent = value;
        wrap.append(dt, dd);
        return wrap;
    }

    function createGuidance(owner, source, initialSlug) {
        const details = document.createElement("details");
        details.className = "h4bs-image-guidance";
        details.dataset.h4bsImageGuidance = "1";
        const summary = document.createElement("summary");
        const title = document.createElement("span");
        title.textContent = labels.title;
        const summaryValue = document.createElement("span");
        summaryValue.className = "h4bs-image-guidance__summary";
        summary.append(title, summaryValue);
        const body = document.createElement("div");
        body.className = "h4bs-image-guidance__body";
        const purpose = document.createElement("label");
        purpose.className = "h4bs-image-guidance__purpose";
        const purposeText = document.createElement("span");
        purposeText.textContent = labels.purpose;
        const select = document.createElement("select");
        Object.keys(profiles).forEach(function (slug) {
            const option = document.createElement("option");
            option.value = slug;
            option.textContent = profiles[slug].label || slug;
            option.selected = slug === initialSlug;
            select.appendChild(option);
        });
        purpose.append(purposeText, select);
        const facts = document.createElement("dl");
        facts.className = "h4bs-image-guidance__facts";
        const advice = document.createElement("p");
        advice.className = "h4bs-image-guidance__advice";
        const status = document.createElement("p");
        status.className = "h4bs-image-guidance__status";
        status.setAttribute("aria-live", "polite");
        const link = document.createElement("a");
        link.className = "h4bs-image-guidance__link";
        link.href = String(config.docsUrl || "https://help4wordpress.com/help4-builder-images-media/");
        link.target = "_blank";
        link.rel = "noopener noreferrer";
        link.textContent = labels.openGuide;
        body.append(purpose, facts, advice, status, link);
        details.append(summary, body);
        owner.appendChild(details);

        const state = { details, summaryValue, select, facts, advice, status, owner, source, token: 0 };
        select.addEventListener("change", function () {
            renderProfile(state);
            inspect(state);
        });
        renderProfile(state);
        inspect(state);
        return state;
    }

    function renderProfile(state) {
        const profile = profiles[state.select.value] || {};
        state.summaryValue.textContent = profileSummary(profile);
        state.facts.innerHTML = "";
        state.facts.append(
            fact(labels.recommended, profileSummary(profile)),
            fact(labels.formats, (profile.formats || []).map(function (item) { return String(item).toUpperCase(); }).join(", ")),
            fact(labels.weight, profile.max_kb ? "Up to " + profile.max_kb + " KB" : "Keep it lightweight")
        );
        state.advice.textContent = profile.advice || "";
    }

    function loadDimensions(url) {
        return new Promise(function (resolve) {
            if (/^attachment:[1-9][0-9]*$/.test(url)) {
                resolve({ url: url, width: 0, height: 0, format: "", ok: false });
                return;
            }
            const image = new Image();
            image.onload = function () {
                resolve({ url: url, width: image.naturalWidth || 0, height: image.naturalHeight || 0, format: extension(url), ok: true });
            };
            image.onerror = function () {
                resolve({ url: url, width: 0, height: 0, format: extension(url), ok: false });
            };
            image.src = url;
        });
    }

    function ratioValue(profile) {
        const width = Number(profile.width || 0);
        const height = Number(profile.height || 0);
        return width > 0 && height > 0 ? width / height : 0;
    }

    function setStatus(state, message, kind) {
        state.status.textContent = message;
        state.status.classList.toggle("is-good", kind === "good");
        state.status.classList.toggle("is-warning", kind === "warning");
    }

    function inspect(state) {
        const token = ++state.token;
        const urls = urlsFrom(state.owner, state.source);
        if (!urls.length) {
            setStatus(state, labels.none, "");
            return;
        }
        setStatus(state, labels.checking, "");
        Promise.all(urls.map(loadDimensions)).then(function (items) {
            if (token !== state.token) return;
            const readable = items.filter(function (item) { return item.ok && item.width > 0 && item.height > 0; });
            if (!readable.length) {
                setStatus(state, labels.unavailable, "warning");
                return;
            }
            const profile = profiles[state.select.value] || {};
            const first = readable[0];
            const messages = [labels.selected + ": " + first.width + " x " + first.height + " px"];
            let warning = false;
            if (Number(profile.width || 0) > 0 && first.width < Number(profile.width)) {
                warning = true;
                messages.push(labels.small);
            }
            if (Number(profile.height || 0) > 0 && first.height < Number(profile.height)) {
                warning = true;
                if (!messages.includes(labels.small)) messages.push(labels.small);
            }
            const targetRatio = ratioValue(profile);
            if (targetRatio > 0 && Math.abs(first.width / first.height - targetRatio) / targetRatio > 0.08) {
                warning = true;
                messages.push(labels.ratio);
            }
            const preferred = (profile.formats || []).map(function (format) { return String(format).toLowerCase(); });
            if (first.format && preferred.length && !preferred.includes(first.format)) {
                warning = true;
                messages.push(labels.format);
            }
            if (readable.length > 1) {
                const ratios = readable.map(function (item) { return item.width / item.height; });
                if (Math.max.apply(Math, ratios) - Math.min.apply(Math, ratios) > 0.12) {
                    warning = true;
                    messages.push(labels.mixed);
                }
                messages[0] += " (" + readable.length + " checked)";
            }
            if (!warning) messages.push(labels.good);
            setStatus(state, messages.join(" "), warning ? "warning" : "good");
        });
    }

    function enhance(element) {
        if (!isImageControl(element) || enhanced.has(element)) return;
        const owner = controlOwner(element);
        if (!owner || owner.querySelector(":scope > .h4bs-image-guidance")) {
            enhanced.add(element);
            return;
        }
        const slug = inferProfile(element);
        if (!slug || !profiles[slug]) return;
        enhanced.add(element);
        const state = createGuidance(owner, element, slug);
        pending.set(owner, state);
    }

    function scan(root) {
        const scope = root instanceof Element || root instanceof Document ? root : document;
        if (scope instanceof Element && isImageControl(scope)) enhance(scope);
        scope.querySelectorAll("[data-h4bs-image-profile], [data-h4bs-media-input], [data-event-image], [data-select-portrait], [data-media-target], input, textarea").forEach(enhance);
    }

    function queueScan() {
        if (scanQueued) return;
        scanQueued = true;
        window.requestAnimationFrame(function () {
            scanQueued = false;
            scan(document);
        });
    }

    document.addEventListener("input", function (event) {
        const owner = event.target instanceof Element ? controlOwner(event.target) : null;
        const state = owner ? pending.get(owner) : null;
        if (state) inspect(state);
    }, true);
    document.addEventListener("change", function (event) {
        const owner = event.target instanceof Element ? controlOwner(event.target) : null;
        const state = owner ? pending.get(owner) : null;
        if (state) inspect(state);
        queueScan();
    }, true);
    document.addEventListener("load", function (event) {
        if (!(event.target instanceof HTMLImageElement)) return;
        const owner = event.target.closest("[data-h4bs-image-profile], [data-h4bs-field-key], .h4bs-slider-field, .h4bs-calendar-image, .h4bs-staff-editor-section, .h4bs-media-control, td, label, p");
        const state = owner ? pending.get(owner) : null;
        if (state) inspect(state);
    }, true);
    document.addEventListener("h4bs:image-guidance-refresh", queueScan);

    new MutationObserver(queueScan).observe(document.documentElement, { childList: true, subtree: true });
    scan(document);
}());
