/**
 * Smart Convert – Currency & Unit Conversion - Inline Format Tool
 *
 * This script registers a custom Format Type ('smart-convert-currency-unit-conversion/convert-inline') in the WordPress 
 * Gutenberg block editor.
 * * @package           smart-convert-currency-unit-conversion
 * @author            Petr Novák
 * @copyright         2025 Petr Novák
 * @license           GPL-2.0-or-later
 * @licenseURI        https://www.gnu.org/licenses/gpl-2.0.html
 */
(function (wp) {
    if ( ! wp || ! wp.richText ) {
        return;
    }

    var registerFormatType = wp.richText.registerFormatType;
    var insert = wp.richText.insert;
    var applyFormat = wp.richText.applyFormat;
    var removeFormat = wp.richText.removeFormat;
    var __ = wp.i18n.__;
    var _x = wp.i18n._x;

    var components = wp.components;
    var RichTextToolbarButton = (wp.blockEditor || wp.editor).RichTextToolbarButton;
    var Modal = components.Modal;
    var TextControl = components.TextControl;
    var Button = components.Button;
    var ComboboxControl = components.ComboboxControl;
    
    var el = wp.element.createElement;
    var Fragment = wp.element.Fragment;
    var useState = wp.element.useState;
    var useEffect = wp.element.useEffect;

    var DEFAULT_CURRENCY = (window.SmartConvertCurrencyUnitConversionSettings && window.SmartConvertCurrencyUnitConversionSettings.defaultCurrency)
        ? String(window.SmartConvertCurrencyUnitConversionSettings.defaultCurrency).trim().toLowerCase()
        : 'usd';
        
    var TEMPLATES_DATA = window.SmartConvertCurrencyUnitConversionSettings || { templates: [], defaultCurrencyKey: 'builtin_4', defaultUnitsKey: 'builtin_4' };

    function tDual(englishMsgid) {
        return __(englishMsgid, 'smart-convert-currency-unit-conversion');
    }

    function txDual(englishMsgid, context) {
        return _x(englishMsgid, context, 'smart-convert-currency-unit-conversion');
    }

    function getTemplateOptions() {
        if (!TEMPLATES_DATA.templates) return [];
        return TEMPLATES_DATA.templates.map(function(template) {
            var label = template.name;
            if (template.type === 'builtin') {
                label += ' [' + tDual('System') + ']';
            }
            if (template.isDefaultCurrency) {
                label += ' [' + tDual('Default for Currencies') + ']';
            }
            if (template.isDefaultUnits) {
                label += ' [' + tDual('Default for Units') + ']';
            }
            return {
                value: template.key,
                label: label
            };
        });
    }

    var CURRENCY_CODES = ['ada','aed','afn','all','amd','aoa','ars','aud','awg','azn','bam','bbd','bdt','bgn','bhd','bif','bmd','bnb','bnd','bob','brl','bsd','btc','btn','bwp','byn','bzd','cad','cdf','chf','clp','cny','cop','crc','cup','cve','czk','djf','dkk','doge','dop','dzd','egp','ern','etb','eth','eur','fjd','fkp','gbp','gel','ghs','gip','gmd','gnf','gtq','gyd','hkd','hnl','htg','huf','idr','ils','inr','iqd','irr','isk','jmd','jod','jpy','kes','kgs','khr','kmf','kpw','krw','kwd','kyd','kzt','lak','lbp','lkr','lrd','lsl','lyd','mad','mdl','mga','mkd','mmk','mnt','mop','mru','mur','mvr','mwk','mxn','myr','mzn','nad','ngn','nio','nok','npr','nzd','omr','pab','pen','pgk','php','pkr','pln','pyg','qar','ron','rsd','rub','rwf','sar','sbd','scr','sdg','sek','sgd','shp','sle','sol','sos','srd','ssp','stn','svc','syp','szl','thb','tjs','tmt','tnd','top','trx','try','ttd','twd','tzs','uah','ugx','usd','usdc','usdt','uyu','uzs','ved','vnd','vuv','wst','xaf','xcd','xcg','xof','xpf','xrp','yer','zar','zmw','zwg'];

    function isCurrencyCode(value) {
        if (!value || !value.trim()) return false;
        var code = value.trim().toLowerCase();
        return CURRENCY_CODES.indexOf(code) !== -1;
    }

    var UNIT_GROUPS = {
        length: ['km','m','dm','cm','mm','in','ft','yard','mi','nmi','angstrom','um','nm'],
        mass: ['t','kg','g','mg','ug','ounce','lb','st','ton_us','ton_uk'],
        volume: ['l','dl','cl','ml','gallon_us','gallon_uk','quart','pint','cups','fl_oz','tbsp','tsp'],
        area: ['sqkm','sqm','sqcm','sqmm','sqin','sqft','sqyd','acre','sqmi','hectare','are'],
        time: ['s','minute','h','day','week','month','year','decade','century'],
        speed: ['mps','kph','mph','fts','kn'],
        pressure: ['pa','kpa','mpa','bar','mbar','atm','psi','torr','mmhg'],
        energy: ['j','kj','calorie','kcal','wh','kwh','btu','ev'],
        power: ['w','kw','mw','hp'],
        density: ['kgcumm','gcumcm','lbcumft'],
        frequency: ['hz','khz','mhz','ghz'],
        data: ['bit','b','kb','mb','gb','tb','pb','kib','mib','gib'],
        angle: ['deg','radian','gradian'],
        temperature: ['C','F','K','R']
    };
    
    var CUSTOM_UNITS_DATA = (window.SmartConvertCurrencyUnitConversionSettings && window.SmartConvertCurrencyUnitConversionSettings.customUnits) ? window.SmartConvertCurrencyUnitConversionSettings.customUnits : [];
    if (CUSTOM_UNITS_DATA.length > 0) {
        UNIT_GROUPS.custom = CUSTOM_UNITS_DATA;
    }

    var UNIT_CODE_GROUP = {};
    (function buildUnitCodeGroup() {
        for (var group in UNIT_GROUPS) {
            if (!UNIT_GROUPS.hasOwnProperty(group)) continue;
            var arr = UNIT_GROUPS[group];
            for (var i = 0; i < arr.length; i++) {
                var item = arr[i];
                var code = (typeof item === 'object' && item !== null && item.value) ? item.value : String(item);
                UNIT_CODE_GROUP[code.toLowerCase()] = group;
            }
        }
    })();

    function getUnitGroup(fromValue) {
        if (!fromValue) return null;
        return UNIT_CODE_GROUP[fromValue.trim().toLowerCase()] || null;
    }
    
    function isUnitCode(value) {
        return !!getUnitGroup(value);
    }
    
    function sameUnitGroup(fromValue, toValue) {
        var g1 = getUnitGroup(fromValue);
        var g2 = getUnitGroup(toValue);
        return !!(g1 && g2 && g1 === g2);
    }

    function getGlobalUnitLabel(code, isCurrency) {
        if (!code) return '';
        var lower = code.toLowerCase();
        var key = 'global-unit-' + lower;
        var unitCatalogEnglishMap = (window.SmartConvertCurrencyUnitConversionSettings && window.SmartConvertCurrencyUnitConversionSettings.unitCatalogEnglishMap)
            ? window.SmartConvertCurrencyUnitConversionSettings.unitCatalogEnglishMap
            : {};
        var englishPhrase = unitCatalogEnglishMap[key] || '';
        var name = englishPhrase ? __(englishPhrase, 'smart-convert-currency-unit-conversion') : '';

        var displayCode = isCurrency ? lower.toUpperCase() : lower;
        if (name && englishPhrase && name !== englishPhrase) {
            return displayCode + ' (' + name + ')';
        }
        return displayCode;
    }

    var CURRENCY_OPTIONS = CURRENCY_CODES.map(function (code) {
        return {
            label: getGlobalUnitLabel(code, true),
            value: code.toUpperCase()
        };
    });

    var ALL_UNIT_OPTIONS = Object.keys(UNIT_GROUPS).reduce(function (acc, group) {
        return acc.concat(
            UNIT_GROUPS[group].map(function (item) {
                if (typeof item === 'object' && item !== null && item.value) {
                    return {
                        label: item.label || item.value,
                        value: item.value
                    };
                }
                return {
                    label: getGlobalUnitLabel(item, false),
                    value: item
                };
            })
        );
    }, []);
    
    function getUnitOptionsForGroup(group, excludeCode) {
        if (!group || !UNIT_GROUPS[group]) return [];
        return UNIT_GROUPS[group].map(function(item) {
            if (typeof item === 'object' && item !== null && item.value) {
                return {
                    label: item.label || item.value,
                    value: item.value
                };
            }
            return {
                label: getGlobalUnitLabel(item, false),
                value: item
            };
        });
    }

    function autoFillToIfCurrency(fromVal, toVal, setTo) {
        var fromTrim = (fromVal || '').trim();
        var toTrim = (toVal || '').trim();
        var defaultCur = DEFAULT_CURRENCY || '';

        if (fromTrim && isCurrencyCode(fromTrim) && defaultCur && !toTrim) {
            setTo(defaultCur.toUpperCase());
        }
    }

    function findConvertShortcodeRange(value) {
        if (!value || typeof value.text !== 'string') return null;
        var text = value.text;
        var caret = (typeof value.start === 'number') ? value.start : 0;
        var regex = /\[smart-convert[^\]]*\]/g;
        var match;

        while ((match = regex.exec(text)) !== null) {
            var start = match.index;
            var end = start + match[0].length;
            if (caret >= start && caret <= end) {
                return {
                    start: start,
                    end: end,
                    shortcode: match[0]
                };
            }
        }
        return null;
    }

    function parseConvertShortcode(shortcode) {
        var result = { amount: '', from: '', to: '', decimals: '', output: '' };
        if (!shortcode || shortcode.indexOf('[smart-convert') === -1) return result;
        var attrString = shortcode.replace(/^\[smart-convert/i, '').replace(/\]$/, '');
        var attrRegex = /([\w-]+)\s*=\s*(["'”″])(.*?)\2/g;
        var match;
        while ((match = attrRegex.exec(attrString)) !== null) {
            result[match[1]] = match[3];
        }
        return result;
    }

    function ensureOptionExists(options, value) {
        if (!value) return options;
        var exists = options.some(function(o) { 
            return o.value.toLowerCase() === value.toLowerCase(); 
        });
        if (!exists) {
            var newOptions = options.slice(); 
            newOptions.unshift({
                value: value,
                label: value + ' (Custom)'
            });
            return newOptions;
        }
        return options;
    }

    /**
     * Main Registration of the Inline Format Type.
     */
    registerFormatType('smart-convert-currency-unit-conversion/convert-inline', {
        title: tDual('Smart Convert - Currency & Unit Conversion'),
        tagName: 'span',
        className: 'smart-convert-currency-unit-conversion-inline',
        isDefault: true,
        edit: function (props) {
            var value = props.value;
            var onChange = props.onChange;
            var isActive = props.isActive;

            var _openState = useState(false);
            var isOpen = _openState[0];
            var setIsOpen = _openState[1];

            var _amountState = useState('1');
            var amount = _amountState[0];
            var setAmount = _amountState[1];

            var _fromState = useState('USD');
            var from = _fromState[0];
            var setFrom = _fromState[1];

            var _toState = useState('');
            var to = _toState[0];
            var setTo = _toState[1];

            var _decimalsState = useState('0');
            var decimals = _decimalsState[0];
            var setDecimals = _decimalsState[1];

            var _outputState = useState('');
            var output = _outputState[0];
            var setOutput = _outputState[1];

            var _editRangeState = useState(null);
            var editRange = _editRangeState[0];
            var setEditRange = _editRangeState[1];

            var _lastAutoOpenedState = useState(null);
            var lastAutoOpenedRange = _lastAutoOpenedState[0];
            var setLastAutoOpenedRange = _lastAutoOpenedState[1];

            useEffect(function() {
                if (!value || !value.text) return;
                var text = value.text;
                var caret = (value && typeof value.start === 'number') ? value.start : null;
                var regex = /\[smart-convert[^\]]*\]/g;
                var match;
                var newValue = value;
                var hasChanges = false;

                while ((match = regex.exec(text)) !== null) {
                    var start = match.index;
                    var end = start + match[0].length;
                    var isCursorInside = (caret !== null && caret >= start && caret <= end);
                    var currentFormats = (newValue.formats && newValue.formats[start]) || [];
                    var isFormatted = currentFormats.some(function(f){ 
                        return f.type === 'smart-convert-currency-unit-conversion/convert-inline'; 
                    });

                    if (isCursorInside) {
                        if (!isFormatted) {
                            newValue = applyFormat(newValue, { type: 'smart-convert-currency-unit-conversion/convert-inline' }, start, end);
                            hasChanges = true;
                        }
                    } else {
                        if (isFormatted) {
                            newValue = removeFormat(newValue, 'smart-convert-currency-unit-conversion/convert-inline', start, end);
                            hasChanges = true;
                        }
                    }
                }

                if (hasChanges) {
                    onChange(newValue);
                }

                var safetyTimer = setTimeout(function() {
                    var cleanValue = removeFormat(value, 'smart-convert-currency-unit-conversion/convert-inline', 0, text.length);
                    if (cleanValue !== value) {
                        onChange(cleanValue);
                    }
                }, 3000);

                return function() { clearTimeout(safetyTimer); };

            }, [value.start, value.end, value.text]);

            useEffect(function() {
                if (isOpen) return; 

                var range = findConvertShortcodeRange(value);
                if (range) {
                    if (!lastAutoOpenedRange || lastAutoOpenedRange.start !== range.start) {
                        setLastAutoOpenedRange(range);
                        openModal();
                    }
                } else {
                    setLastAutoOpenedRange(null);
                }
            }, [value.start, value.end]);

            function parseSelection(text) {
                if (!text) return null;
                var numMatch = text.match(/[\d]+([.,][\d]+)?/); 
                if (!numMatch) return null;
                var amount = numMatch[0];
                var unitRaw = text.replace(amount, '').trim();
                var unit = unitRaw.replace(/[^a-zA-Z]/g, '');
                return { amount: amount, unit: unit };
            }

            function openModal() {
                var range = findConvertShortcodeRange(value);
                
                if (range) {
                    var parsed = parseConvertShortcode(range.shortcode);
                    var rawFrom = parsed.from || 'USD';
                    var normFrom = rawFrom;
                    
                    if (isCurrencyCode(rawFrom)) {
                        normFrom = rawFrom.toUpperCase();
                    } else {
                        var matchFrom = ALL_UNIT_OPTIONS.find(function(u) {
                             return u.value.toLowerCase() === rawFrom.toLowerCase();
                        });
                        if (matchFrom) normFrom = matchFrom.value;
                        else normFrom = rawFrom; 
                    }

                    var rawTo = parsed.to || '';
                    var normTo = rawTo;
                    if (rawTo) {
                        if (isCurrencyCode(rawTo)) {
                            normTo = rawTo.toUpperCase();
                        } else {
                            var matchTo = ALL_UNIT_OPTIONS.find(function(u) {
                                return u.value.toLowerCase() === rawTo.toLowerCase();
                            });
                            if (matchTo) normTo = matchTo.value;
                            else normTo = rawTo; 
                        }
                    }
                    
                    setAmount(parsed.amount || '1');
                    setFrom(normFrom);
                    setTo(normTo);
                    setDecimals(parsed.decimals || '0');
                    setOutput(parsed.output || '');
                    setEditRange({ start: range.start, end: range.end });
                } else {
                    var defaultAmount = '1';
                    var defaultFrom = 'USD'; 
                    var defaultTo = '';
                    var selectedText = value.text.slice(value.start, value.end);
                    
                    if (selectedText && selectedText.trim().length > 0) {
                        var parsedSelection = parseSelection(selectedText);
                        if (parsedSelection) {
                            defaultAmount = parsedSelection.amount;
                            if (parsedSelection.unit) {
                                var foundUnit = parsedSelection.unit;
                                if (isCurrencyCode(foundUnit)) {
                                    defaultFrom = foundUnit.toUpperCase();
                                } else if (isUnitCode(foundUnit)) {
                                    var group = getUnitGroup(foundUnit);
                                    if (group) {
                                         var unitOptions = getUnitOptionsForGroup(group);
                                         var match = unitOptions.find(function(u) { 
                                             return u.value.toLowerCase() === foundUnit.toLowerCase(); 
                                         });
                                         if (match) defaultFrom = match.value;
                                    }
                                }
                            }
                        }
                    }

                    setAmount(defaultAmount);
                    setFrom(defaultFrom);
                    setTo(defaultTo);
                    setDecimals('0');
                    setOutput(TEMPLATES_DATA.defaultCurrencyKey || '');
                    setEditRange(null);
                    autoFillToIfCurrency(defaultFrom, '', setTo);
                }
                setIsOpen(true);
            }

            function closeModal() { setIsOpen(false); }

            function insertShortcode() {
                var amountTrim = (amount || '').trim();
                var fromTrim = (from || '').trim();
                var toTrim = (to || '').trim();
                var decimalsTrim = (decimals || '').trim();
                var outputTrim = (output || '').trim();

                if (!amountTrim || !/^[0-9.,]+$/.test(amountTrim)) {
                    window.alert(tDual('The conversion value field is filled in incorrectly.'));
                    return;
                }

                var fromIsCurrency = isCurrencyCode(fromTrim);
                var fromUnitGroup = getUnitGroup(fromTrim);

                if (!fromIsCurrency && !fromUnitGroup && fromTrim.length === 0) {
                    window.alert(txDual('The original currency or unit field is filled in incorrectly.', 'label.form.source_unit'));
                    return;
                }

                if (fromIsCurrency) {
                    if (toTrim && !isCurrencyCode(toTrim) && isUnitCode(toTrim)) {
                        window.alert(txDual('The target currency or unit field is filled in incorrectly.', 'label.form.target_unit'));
                        return;
                    }
                } else if (fromUnitGroup) {
                    var isCustomConversion = (fromUnitGroup === 'custom' || getUnitGroup(toTrim) === 'custom');
                    if (!toTrim || (isUnitCode(toTrim) && !sameUnitGroup(fromTrim, toTrim) && !isCustomConversion)) {
                        if (isUnitCode(toTrim) && !sameUnitGroup(fromTrim, toTrim)) {
                             window.alert(txDual('The target currency or unit field is filled in incorrectly.', 'label.form.target_unit'));
                             return;
                        }
                    }
                }

                var shortcode = '[smart-convert';
                shortcode += ' amount="' + amountTrim + '"';
                shortcode += ' from="' + fromTrim + '"';
                if (toTrim) shortcode += ' to="' + toTrim + '"';
                shortcode += ' decimals="' + decimalsTrim + '"';
                if (outputTrim) shortcode += ' output="' + outputTrim + '"';
                shortcode += ']';

                var newValue;
                var startPos;
                if (editRange) {
                    var v = Object.assign({}, value, { start: editRange.start, end: editRange.end });
                    startPos = editRange.start;
                    newValue = insert(v, shortcode);
                } else {
                    startPos = typeof value.start === 'number' ? value.start : 0;
                    newValue = insert(value, shortcode);
                }

                var endPos = startPos + shortcode.length;
                newValue = applyFormat(newValue, { type: 'smart-convert-currency-unit-conversion/convert-inline' }, startPos, endPos);

                onChange(newValue);
                setIsOpen(false);
                setEditRange(null);
            }

            var fromTrim = (from || '').trim();
            var fromIsCurrency = isCurrencyCode(fromTrim);
            var fromUnitGroup = getUnitGroup(fromTrim);
            var fromOptions = ensureOptionExists(CURRENCY_OPTIONS.concat(ALL_UNIT_OPTIONS), from);
            var toOptions = [];
            
            if (fromIsCurrency) toOptions = CURRENCY_OPTIONS;
            else if (fromUnitGroup) toOptions = getUnitOptionsForGroup(fromUnitGroup);
            else toOptions = CURRENCY_OPTIONS.concat(ALL_UNIT_OPTIONS);

            toOptions = ensureOptionExists(toOptions, to);

            return el(
                Fragment,
                null,
                el(RichTextToolbarButton, {
                    icon: 'money-alt',
                    title: tDual('Smart Convert - Currency & Unit Conversion'),
                    onClick: openModal,
                    isActive: isActive
                }),
                isOpen && el(Modal, {
                    className: 'smart-convert-currency-unit-conversion-inline-modal',
                    title: tDual('Smart Convert - Currency & Unit Conversion'),
                    onRequestClose: closeModal,
                    style: { width: '600px', maxWidth: '90vw', maxHeight: '95vh' }
                },
                el('div', { className: 'smart-convert-currency-unit-conversion-inline-content-wrapper' },
                    el('div', { className: 'components-base-control smart-convert-currency-unit-conversion-field-wrapper' },
                        el('div', { className: 'components-base-control__field' },
                            el('label', { className: 'components-base-control__label smart-convert-currency-unit-conversion-field-label' }, tDual('Amount')),
                            el('div', { className: 'smart-convert-currency-unit-conversion-inline-hint smart-convert-currency-unit-conversion-field-hint' }, tDual('Enter the value or amount to be converted to another unit or currency. Use a period to separate decimal places.')),
                            el(TextControl, { label: '', value: amount, onChange: setAmount })
                        )
                    ),
                    el('div', { className: 'components-base-control smart-convert-currency-unit-conversion-field-wrapper' },
                        el('div', { className: 'components-base-control__field' },
                            el('label', { className: 'components-base-control__label smart-convert-currency-unit-conversion-field-label' }, txDual('Original Currency / Unit', 'label.form.source_unit')),
                            el('div', { className: 'smart-convert-currency-unit-conversion-inline-hint smart-convert-currency-unit-conversion-field-hint' }, tDual('Select the name of the currency or unit to be converted to another currency or unit from the autosuggest.')),
                            el(ComboboxControl, {
                                label: '', value: from,
                                onChange: function (val) {
                                    var newFrom = val || '';
                                    setFrom(newFrom);
                                    autoFillToIfCurrency(newFrom, to, setTo);
                                },
                                options: fromOptions
                            })
                        )
                    ),
                    el('div', { className: 'components-base-control smart-convert-currency-unit-conversion-field-wrapper' },
                        el('div', { className: 'components-base-control__field' },
                            el('label', { className: 'components-base-control__label smart-convert-currency-unit-conversion-field-label' }, txDual('Target Currency / Unit', 'label.form.target_unit')),
                            el('div', { className: 'smart-convert-currency-unit-conversion-inline-hint smart-convert-currency-unit-conversion-field-hint' }, tDual('Select the name of the currency or unit to which the value should be converted from the autosuggest.')),
                            el(ComboboxControl, {
                                label: '', value: to,
                                onChange: function (val) { setTo(val || ''); },
                                options: toOptions
                            })
                        )
                    ),
                    el('div', { className: 'components-base-control smart-convert-currency-unit-conversion-field-wrapper' },
                        el('div', { className: 'components-base-control__field' },
                            el('label', { className: 'components-base-control__label smart-convert-currency-unit-conversion-field-label' }, tDual('Number of Decimal Places')),
                            el('div', { className: 'smart-convert-currency-unit-conversion-inline-hint smart-convert-currency-unit-conversion-field-hint' }, tDual('Enter the number of decimal places to display in the conversion. To round to whole numbers, enter 0.')),
                            el(TextControl, { label: '', value: decimals, onChange: setDecimals })
                        )
                    ),
                    el('div', { className: 'components-base-control smart-convert-currency-unit-conversion-field-wrapper' },
                        el('div', { className: 'components-base-control__field' },
                            el('label', { className: 'components-base-control__label smart-convert-currency-unit-conversion-field-label' }, tDual('Conversion Display Format')),
                            el('div', { className: 'smart-convert-currency-unit-conversion-inline-hint smart-convert-currency-unit-conversion-field-hint' }, tDual('Select the name of the template to be used for displaying the conversion from the autosuggest.')),
                            el(ComboboxControl, {
                                label: '', value: output, onChange: setOutput,
                                options: getTemplateOptions(),
                                placeholder: tDual('Select the name of the template to be used for displaying the conversion from the autosuggest.')
                            })
                        )
                    ),
                    el('div', { style: { marginTop: '1rem', textAlign: 'right' } },
                        el(Button, { isSecondary: true, onClick: closeModal, style: { marginRight: '0.5rem' } }, txDual('Cancel', 'action.cancel.gutenberg')),
                        el(Button, { isPrimary: true, onClick: insertShortcode }, tDual('Insert Conversion'))
                    )
                ))
            );
        }
    });
})(window.wp);