(function($) {
    "use strict";

    var palettes = [
        "#007BFF", "#6610F2", "#6F42C1", "#E83E8C", "#DC3545", "#FD7E14", "#FFC107",
        "#28A745", "#20C997", "#17A2B8", "#6C757D", "#343A40", "#F8F9FA", "#343A40",
        "#FFFFFF", "#333333"
    ];

    function normalizeSymbol(value) {
        var symbol = String(value || "").trim().toLocaleUpperCase();

        try {
            return symbol.replace(/[^\p{L}\p{N}._$-]/gu, "");
        } catch (error) {
            return symbol.toUpperCase().replace(/[^A-Z0-9._$-]/g, "");
        }
    }

    function normalizeSymbolList(value, fallback) {
        var items = String(value || "").split(",");
        var normalized = [];

        $.each(items, function(_, item) {
            var symbol = normalizeSymbol(item);

            if (symbol) {
                normalized.push(symbol);
            }
        });

        return normalized.length ? normalized : fallback.slice();
    }

    function normalizeHex(value, fallback) {
        var normalized = String(value || "").replace(/^#/, "").toLowerCase().replace(/[^a-f0-9]/g, "");

        if (/^([a-f0-9]{3}|[a-f0-9]{6})$/.test(normalized)) {
            return normalized;
        }

        return fallback;
    }

    function collectSelected($select, fallback) {
        var values = $select.find("option:selected").map(function() {
            return normalizeSymbol(this.value);
        }).get();

        values = $.grep(values, function(value) {
            return !!value;
        });

        return values.length ? values : fallback.slice();
    }

    function checkboxValue($form, selector) {
        return $form.find(selector).is(":checked") ? "1" : "0";
    }

    function collectState($form) {
        var defaultBase = normalizeSymbolList($form.data("defaultBase"), ["USD", "EUR", "CNY", "GBP"]);
        var defaultItems = normalizeSymbolList($form.data("defaultItems"), ["BTC", "ETH", "LTC", "XMR", "DASH"]);

        return {
            base: collectSelected($form.find("[name='base']"), defaultBase),
            items: collectSelected($form.find("[name='items']"), defaultItems),
            backgroundColor: normalizeHex($form.find("[name='backgroundColor']").val(), "fff"),
            streaming: checkboxValue($form, "[name='streaming']"),
            striped: checkboxValue($form, "[name='striped']"),
            rounded: checkboxValue($form, "[name='rounded']"),
            boxShadow: checkboxValue($form, "[name='boxShadow']"),
            border: checkboxValue($form, "[name='border']"),
            signature: checkboxValue($form, "[name='signature']")
        };
    }

    function buildShortcode($form, state) {
        var slug = $form.data("slug") || "cryptocurrency-price-widget";
        var attributes = [
            'base="' + state.base.join(",") + '"',
            'items="' + state.items.join(",") + '"',
            'backgroundColor="' + state.backgroundColor + '"',
            'streaming="' + state.streaming + '"',
            'striped="' + state.striped + '"',
            'rounded="' + state.rounded + '"',
            'boxShadow="' + state.boxShadow + '"',
            'border="' + state.border + '"',
            'signature="' + state.signature + '"'
        ];

        return "[" + slug + " " + attributes.join(" ") + "]";
    }

    function buildPreviewUrl($form, state) {
        var baseUrl = String($form.data("previewUrlBase") || "");
        var query;

        if (!baseUrl) {
            return "";
        }

        query = $.param({
            base: state.base.join(","),
            items: state.items.join(","),
            backgroundColor: state.backgroundColor,
            streaming: state.streaming,
            striped: state.striped,
            rounded: state.rounded,
            boxShadow: state.boxShadow,
            border: state.border,
            signature: "0"
        });

        return baseUrl + "?" + query;
    }

    function updateOutputs($form) {
        var state = collectState($form);
        var previewUrl = buildPreviewUrl($form, state);
        var $root = $form.closest(".crcpw-admin-page");
        var $preview = $root.find("#crcpw-widget-preview");
        var $previewAttribution = $root.find("#crcpw-widget-preview-attribution");
        var $code = $root.find("#crcpw-widget-code");
        var $copyStatus = $root.find("#crcpw-copy-shortcode-status");

        if ($code.length) {
            $code.val(buildShortcode($form, state));
        }

        if ($copyStatus.length) {
            $copyStatus.text("").removeClass("is-error is-success");
        }

        if ($preview.length && previewUrl) {
            $preview.attr({
                src: previewUrl,
                scrolling: "no"
            });
        }

        if ($previewAttribution.length) {
            $previewAttribution.toggle(state.signature === "1");
        }
    }

    function initColorPicker($form) {
        var $field = $form.find(".color-field");

        if (!$field.length) {
            return;
        }

        if (typeof $.fn.wpColorPicker !== "function") {
            $field.on("input.crcpwSettings change.crcpwSettings", function() {
                updateOutputs($form);
            });
            return;
        }

        try {
            $field.wpColorPicker({
                defaultColor: false,
                change: function() {
                    updateOutputs($form);
                },
                clear: function() {
                    updateOutputs($form);
                },
                hide: true,
                palettes: palettes
            });
        } catch (error) {
            $field.on("input.crcpwSettings change.crcpwSettings", function() {
                updateOutputs($form);
            });
        }
    }

    function initTokenPickers($form) {
        if (!window.CRCPWTokenPicker || typeof window.CRCPWTokenPicker.init !== "function") {
            return;
        }

        try {
            window.CRCPWTokenPicker.init($form.find("[name='base']"), {
                limit: 4,
                type: "flags",
                allowCustom: false,
                placeholder: $form.data("baseSearch")
            });
        } catch (error) {
            window.console && console.warn && console.warn("CRCPW base picker init failed", error);
        }

        try {
            window.CRCPWTokenPicker.init($form.find("[name='items']"), {
                limit: 15,
                type: "coins",
                allowCustom: true,
                placeholder: $form.data("itemsSearch")
            });
        } catch (error) {
            window.console && console.warn && console.warn("CRCPW item picker init failed", error);
        }
    }

    function bindPreviewResize($form) {
        var previewBase = String($form.data("previewUrlBase") || "");
        var previewOrigin = "";
        var $root = $form.closest(".crcpw-admin-page");

        if (previewBase) {
            try {
                previewOrigin = new window.URL(previewBase).origin;
            } catch (error) {
                previewOrigin = "";
            }
        }

        $(window).on("message.crcpwSettings", function(event) {
            var payload = event.originalEvent ? event.originalEvent.data : null;
            var origin = event.originalEvent ? event.originalEvent.origin : "";
            var data;

            if (previewOrigin && origin && previewOrigin !== origin) {
                return;
            }

            if (typeof payload !== "string") {
                return;
            }

            try {
                data = JSON.parse(payload);
            } catch (error) {
                return;
            }

            if (data && data.h) {
                $root.find("#crcpw-widget-preview").height(parseInt(data.h, 10) + 15);
            }
        });
    }

    function bindFormUpdates($form) {
        $form.on("change.crcpwSettings", "select, input[type='checkbox']", function() {
            updateOutputs($form);
        });

        $form.on("input.crcpwSettings change.crcpwSettings", "input[type='number']", function() {
            updateOutputs($form);
        });
    }

    function fallbackCopyText($field) {
        var element = $field.get(0);
        var copied = false;
        var selectionStart;
        var selectionEnd;

        if (!element || typeof element.select !== "function") {
            return false;
        }

        selectionStart = element.selectionStart;
        selectionEnd = element.selectionEnd;

        element.focus();
        element.select();

        try {
            copied = document.execCommand("copy");
        } catch (error) {
            copied = false;
        }

        if (typeof element.setSelectionRange === "function" && typeof selectionStart === "number" && typeof selectionEnd === "number") {
            element.setSelectionRange(selectionStart, selectionEnd);
        }

        return copied;
    }

    function copyShortcode($root) {
        var $field = $root.find("#crcpw-widget-code");
        var $button = $root.find("#crcpw-copy-shortcode");
        var $status = $root.find("#crcpw-copy-shortcode-status");
        var shortcode = $field.val();
        var copiedLabel = $button.data("copiedLabel") || "Copied.";
        var failedLabel = $button.data("copyFailedLabel") || "Copy failed. Select the shortcode and copy it manually.";
        var clipboard = window.navigator && window.navigator.clipboard;
        var showStatus = function(message, isError) {
            if (!$status.length) {
                return;
            }

            $status
                .text(message)
                .toggleClass("is-error", !!isError)
                .toggleClass("is-success", !isError);
        };

        if (!$field.length || !shortcode) {
            showStatus(failedLabel, true);
            return;
        }

        if (clipboard && typeof clipboard.writeText === "function") {
            clipboard.writeText(shortcode).then(function() {
                showStatus(copiedLabel, false);
            }).catch(function() {
                var fallbackCopied = fallbackCopyText($field);

                showStatus(fallbackCopied ? copiedLabel : failedLabel, !fallbackCopied);
            });
            return;
        }

        var fallbackCopied = fallbackCopyText($field);

        showStatus(fallbackCopied ? copiedLabel : failedLabel, !fallbackCopied);
    }

    function bindShortcodeCopy($root) {
        $root.on("click.crcpwSettings", "#crcpw-copy-shortcode", function(event) {
            event.preventDefault();
            copyShortcode($root);
        });
    }

    $(function() {
        var $root = $(".crcpw-admin-page").first();
        var $form = $root.find("form#crcpw-widget-settings");

        if (!$root.length || !$form.length) {
            return;
        }

        initColorPicker($form);
        initTokenPickers($form);
        bindFormUpdates($form);
        bindPreviewResize($form);
        bindShortcodeCopy($root);
        updateOutputs($form);
    });
})(jQuery);
