(function (window, $) {
    'use strict';

    if (!$) {
        return;
    }

    var modules = window.noesiaSwaAdminModules || (window.noesiaSwaAdminModules = {});
    var state = {
        booted: false,
        busy: false,
        listenersBound: false,
        cacheKey: '',
        cachedSettings: null,
        cachedForecast: null
    };

    function getVars() {
        return (window.noesia_admin_vars && typeof window.noesia_admin_vars === 'object') ? window.noesia_admin_vars : {};
    }

    function getOptionStore() {
        var vars = getVars();
        var direct = (vars.options && typeof vars.options === 'object') ? vars.options : {};
        var nested = (direct.noesia_swa_options && typeof direct.noesia_swa_options === 'object') ? direct.noesia_swa_options : {};
        return Object.keys(nested).length ? nested : direct;
    }

    function readFieldValue(selector) {
        var $field = $(selector).first();
        return $field.length ? String($field.val() || '').trim() : '';
    }

    function normalizeLang(value) {
        var lang = String(value || 'en').toLowerCase().trim();
        return ['en', 'el', 'fr', 'de', 'it', 'es'].indexOf(lang) !== -1 ? lang : 'en';
    }

    function getLang() {
        var opt = options();
        var lang = (
            readFieldValue('select[name="noesia_swa_options[news_language]"]') ||
            readFieldValue('select[name="noesia_swa_options[weather_language]"]') ||
            opt.news_language ||
            opt.weather_language ||
            'en'
        );

        lang = String(lang || 'en').toLowerCase();

        if (['en', 'el', 'fr', 'de', 'it', 'es'].indexOf(lang) === -1) {
            lang = 'en';
        }

        return lang;
    }

    function getLocaleFromLang(lang) {
        var map = {
            en: 'en-US',
            el: 'el-GR',
            fr: 'fr-FR',
            de: 'de-DE',
            it: 'it-IT',
            es: 'es-ES'
        };

        return map[normalizeLang(lang)] || 'en-US';
    }

    function getWeekdayNames(lang) {
        var map = {
            en: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
            el: ['Κυρ', 'Δευ', 'Τρι', 'Τετ', 'Πεμ', 'Παρ', 'Σαβ'],
            fr: ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'],
            de: ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa'],
            it: ['Dom', 'Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab'],
            es: ['Dom', 'Lun', 'Mar', 'Mié', 'Jue', 'Vie', 'Sáb']
        };

        return map[normalizeLang(lang)] || map.en;
    }

    function tLabel(key) {
        var lang = getLang();
        var dict = {
            today:       { en: 'Today',       el: 'Σήμερα',       fr: "Aujourd'hui", de: 'Heute', it: 'Oggi', es: 'Hoy' },
            rain:        { en: 'Rain',        el: 'Βροχή',        fr: 'Pluie',       de: 'Regen', it: 'Pioggia', es: 'Lluvia' },
            min:         { en: 'Min',         el: 'Ελάχ.',        fr: 'Min',         de: 'Min',   it: 'Min',     es: 'Mín.' },
            max:         { en: 'Max',         el: 'Μέγ.',         fr: 'Max',         de: 'Max',   it: 'Max',     es: 'Máx.' },
            unavailable: { en: 'Unavailable', el: 'Μη διαθέσιμο', fr: 'Indisponible', de: 'Nicht verfügbar', it: 'Non disponibile', es: 'No disponible' },
            weather:     { en: 'Weather',     el: 'Καιρός',       fr: 'Météo',       de: 'Wetter', it: 'Meteo', es: 'Tiempo' }
        };

        return dict[key] && dict[key][lang] ? dict[key][lang] : (dict[key] ? dict[key].en : key);
    }

    function tr(en, el, fr, de, it, es) {
        var lang = getLang();
        if (lang === 'el') return el;
        if (lang === 'fr') return fr;
        if (lang === 'de') return de;
        if (lang === 'it') return it;
        if (lang === 'es') return es;
        return en;
    }

    function weatherLabel(code) {
        var c = parseInt(code || 0, 10);

        if (c === 0) return tr('Clear', 'Αίθριος', 'Dégagé', 'Klar', 'Sereno', 'Despejado');
        if (c === 1) return tr('Mainly clear', 'Κυρίως αίθριος', 'Plutôt dégagé', 'Überwiegend klar', 'Prevalentemente sereno', 'Mayormente despejado');
        if (c === 2) return tr('Partly cloudy', 'Μερικώς συννεφιασμένος', 'Partiellement nuageux', 'Teilweise bewölkt', 'Parzialmente nuvoloso', 'Parcialmente nublado');
        if (c === 3) return tr('Cloudy', 'Συννεφιασμένος', 'Nuageux', 'Bewölkt', 'Nuvoloso', 'Nublado');
        if (c === 45 || c === 48) return tr('Fog', 'Ομίχλη', 'Brouillard', 'Nebel', 'Nebbia', 'Niebla');
        if (c === 51 || c === 53 || c === 55 || c === 56 || c === 57) return tr('Drizzle', 'Ψιχάλες', 'Bruine', 'Nieselregen', 'Pioviggine', 'Llovizna');
        if (c === 61 || c === 63 || c === 65 || c === 66 || c === 67 || c === 80 || c === 81 || c === 82) return tr('Rain', 'Βροχή', 'Pluie', 'Regen', 'Pioggia', 'Lluvia');
        if (c === 71 || c === 73 || c === 75 || c === 77 || c === 85 || c === 86) return tr('Snow', 'Χιόνι', 'Neige', 'Schnee', 'Neve', 'Nieve');
        if (c === 95 || c === 96 || c === 99) return tr('Storm', 'Καταιγίδα', 'Orage', 'Gewitter', 'Temporale', 'Tormenta');

        return tLabel('weather');
    }

    function weatherEmoji(code) {
        var c = parseInt(code || 0, 10);

        if (c === 0) return '☀️';
        if (c === 1) return '🌤️';
        if (c === 2) return '⛅';
        if (c === 3) return '☁️';
        if (c === 45 || c === 48) return '🌫️';
        if (c === 51 || c === 53 || c === 55 || c === 56 || c === 57) return '🌦️';
        if (c === 61 || c === 63 || c === 65 || c === 66 || c === 67 || c === 80 || c === 81 || c === 82) return '🌧️';
        if (c === 71 || c === 73 || c === 75 || c === 77) return '❄️';
        if (c === 85 || c === 86) return '🌨️';
        if (c === 95 || c === 96 || c === 99) return '⛈️';

        return '☁️';
    }

    function escHtml(input) {
        return String(input || '')
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#039;');
    }

    function weatherSettings() {
        var opt = getOptionStore();
        var units = String(
            readFieldValue('select[name="noesia_swa_options[weather_units]"]') ||
            opt.weather_units ||
            'metric'
        ).trim().toLowerCase();

        if (units !== 'imperial') {
            units = 'metric';
        }

        return {
            city: String(readFieldValue('input[name="noesia_swa_options[weather_city]"]') || opt.weather_city || '').trim(),
            country: String(readFieldValue('input[name="noesia_swa_options[weather_country]"]') || opt.weather_country || '').trim(),
            units: units
        };
    }

    function getWeatherCacheKey(settings, lang) {
        return [
            String(settings.city || '').toLowerCase(),
            String(settings.country || '').toLowerCase(),
            String(settings.units || 'metric').toLowerCase(),
            normalizeLang(lang)
        ].join('|');
    }

    function cacheWeatherData(settings, lang, forecast) {
        state.cacheKey = getWeatherCacheKey(settings, lang);
        state.cachedSettings = {
            city: String(settings.city || ''),
            country: String(settings.country || ''),
            units: String(settings.units || 'metric')
        };
        state.cachedForecast = forecast || null;
    }

    function clearWeatherCache() {
        state.cacheKey = '';
        state.cachedSettings = null;
        state.cachedForecast = null;
    }

    function getWeatherPanel(preferredTarget) {
        var targets = [];
        var seen = {};
        function pushTarget(name) {
            name = String(name || '').trim();
            if (name && !seen[name]) {
                seen[name] = true;
                targets.push(name);
            }
        }

        pushTarget(preferredTarget);
        pushTarget($('#noesia-swa-wrapper').attr('data-noesia-swa-section') || '');
        pushTarget('dashboard');
        pushTarget('weather');

        for (var i = 0; i < targets.length; i += 1) {
            var $panel = $('[data-noesia-swa-section-panel="' + targets[i] + '"]').first();
            if ($panel.length && $panel.find('#noesia-swa-weather-current-main, #noesia-swa-weather-forecast').length) {
                return $panel;
            }
        }

        return $('[data-noesia-swa-section-panel]').has('#noesia-swa-weather-current-main, #noesia-swa-weather-forecast').first();
    }

    function getWeatherNodes(preferredTarget) {
        var $panel = getWeatherPanel(preferredTarget);

        if (!$panel.length) {
            return null;
        }

        return {
            panel: $panel,
            pulse: $panel.find('#noesia-swa-weather-current').first(),
            main: $panel.find('#noesia-swa-weather-current-main').first(),
            briefing: $panel.find('#noesia-swa-morning-briefing-weather-current').first(),
            forecast: $panel.find('#noesia-swa-weather-forecast').first()
        };
    }

    function hasWeatherUi(preferredTarget) {
        var nodes = getWeatherNodes(preferredTarget);
        return !!(nodes && (nodes.pulse.length || nodes.main.length || nodes.briefing.length || nodes.forecast.length));
    }

    function hasExistingWeatherData(preferredTarget) {
        var nodes = getWeatherNodes(preferredTarget);
        var unavailable = tLabel('unavailable').toLowerCase();

        if (!nodes) {
            return false;
        }

        if (nodes.forecast.find('.noesia-swa-forecast-box').length > 0) {
            return true;
        }

        if (nodes.main.find('.noesia-swa-weather-big').text().trim() && nodes.main.text().toLowerCase().indexOf(unavailable) === -1) {
            return true;
        }

        if (nodes.pulse.text().trim() && nodes.pulse.text().toLowerCase().indexOf(unavailable) === -1) {
            return true;
        }

        return false;
    }

    function renderUnavailable(preferredTarget) {
        var nodes = getWeatherNodes(preferredTarget);
        var unavailable = tLabel('unavailable');

        if (!nodes) {
            return;
        }

        if (nodes.pulse.length) {
            nodes.pulse.html('<p><strong>☁️ —</strong></p><p class="noesia-swa-muted">' + escHtml(unavailable) + '</p>');
        }

        if (nodes.main.length) {
            nodes.main.html(
                '<div class="noesia-swa-weather-today-card">' +
                    '<div class="noesia-swa-weather-day">' + escHtml(tLabel('today')) + '</div>' +
                    '<div class="noesia-swa-weather-big">☁️ —</div>' +
                    '<div class="noesia-swa-weather-condition">' + escHtml(unavailable) + '</div>' +
                '</div>'
            );
        }

        if (nodes.briefing.length) {
            nodes.briefing.html('<strong>☁️ —</strong>');
        }

        if (nodes.forecast.length) {
            nodes.forecast.html('');
        }
    }

    function fetchJson(url, timeoutMs) {
        timeoutMs = timeoutMs || 6000;

        if (!window.fetch || !window.AbortController) {
            return Promise.reject(new Error('fetch_unavailable'));
        }

        var controller = new AbortController();
        var timer = window.setTimeout(function () {
            controller.abort();
        }, timeoutMs);

        return window.fetch(url, { method: 'GET', signal: controller.signal })
            .then(function (resp) {
                window.clearTimeout(timer);
                if (!resp || !resp.ok) {
                    return Promise.reject(new Error('http_' + (resp ? resp.status : 0)));
                }
                return resp.json();
            })
            .catch(function (err) {
                window.clearTimeout(timer);
                return Promise.reject(err);
            });
    }

    function buildForecastUrl(lat, lon, settings) {
        var tempUnit = settings.units === 'imperial' ? 'fahrenheit' : 'celsius';

        return 'https://api.open-meteo.com/v1/forecast' +
            '?latitude=' + encodeURIComponent(lat) +
            '&longitude=' + encodeURIComponent(lon) +
            '&current_weather=true' +
            '&daily=weathercode,temperature_2m_max,temperature_2m_min,precipitation_probability_max' +
            '&temperature_unit=' + encodeURIComponent(tempUnit) +
            '&timezone=auto';
    }

    function formatDay(dateString, lang) {
        var date = new Date(String(dateString || '') + 'T12:00:00');
        var locale = getLocaleFromLang(lang);
        var weekdays = getWeekdayNames(lang);
        var weekdayIndex = date.getDay();

        return {
            weekday: weekdays[typeof weekdayIndex === 'number' ? weekdayIndex : 0] || weekdays[0],
            date: date.toLocaleDateString(locale, { day: 'numeric', month: 'short' })
        };
    }

    function renderWeather(forecast, settings, preserveExistingUi, preferredTarget) {
        var nodes = getWeatherNodes(preferredTarget);
        var lang = getLang();
        var unit = settings.units === 'imperial' ? '°F' : '°C';
        var html = '';

        if (!nodes) {
            return false;
        }

        if (!forecast || !forecast.daily || !Array.isArray(forecast.daily.time) || !forecast.daily.time.length) {
            if (!preserveExistingUi && !hasExistingWeatherData(preferredTarget)) {
                renderUnavailable(preferredTarget);
            }
            return false;
        }

        var currentCode = forecast.current_weather ? parseInt(forecast.current_weather.weathercode || 0, 10) : 0;
        var currentTemp = forecast.current_weather ? Math.round(parseFloat(forecast.current_weather.temperature || 0)) : 0;
        var todayHigh = Math.round(parseFloat((forecast.daily.temperature_2m_max || [])[0] || currentTemp));
        var todayLow = Math.round(parseFloat((forecast.daily.temperature_2m_min || [])[0] || currentTemp));
        var todayRain = '—';
        var place = settings.city ? settings.city + (settings.country ? ', ' + settings.country : '') : '';
        var currentLine = weatherEmoji(currentCode) + ' ' + currentTemp + unit;
        var condition = weatherLabel(currentCode);

        if (forecast.daily.precipitation_probability_max && typeof forecast.daily.precipitation_probability_max[0] !== 'undefined') {
            todayRain = Math.max(0, Math.round(parseFloat(forecast.daily.precipitation_probability_max[0] || 0))) + '%';
        }

        if (nodes.pulse.length) {
            nodes.pulse.html(
                '<p><strong>' + escHtml(currentLine + ' · ' + condition) + '</strong></p>' +
                (place ? '<p class="noesia-swa-muted">' + escHtml(place) + '</p>' : '')
            );
        }

        if (nodes.main.length) {
            nodes.main.html(
                '<div class="noesia-swa-weather-today-card">' +
                    '<div class="noesia-swa-weather-day">' + escHtml(tLabel('today')) + '</div>' +
                    (place ? '<div class="noesia-swa-weather-place">' + escHtml(place) + '</div>' : '') +
                    '<div class="noesia-swa-weather-big">' + escHtml(currentLine) + '</div>' +
                    '<div class="noesia-swa-weather-condition">' + escHtml(condition) + '</div>' +
                    '<div class="noesia-swa-weather-today-meta">' +
                        '<div class="noesia-swa-weather-meta-item">' +
                            '<span class="noesia-swa-weather-meta-label">' + escHtml(tLabel('min')) + '</span>' +
                            '<strong class="noesia-swa-weather-meta-value">' + escHtml(todayLow + unit) + '</strong>' +
                        '</div>' +
                        '<div class="noesia-swa-weather-meta-item">' +
                            '<span class="noesia-swa-weather-meta-label">' + escHtml(tLabel('max')) + '</span>' +
                            '<strong class="noesia-swa-weather-meta-value">' + escHtml(todayHigh + unit) + '</strong>' +
                        '</div>' +
                        '<div class="noesia-swa-weather-meta-item">' +
                            '<span class="noesia-swa-weather-meta-label">' + escHtml(tLabel('rain')) + '</span>' +
                            '<strong class="noesia-swa-weather-meta-value">' + escHtml(todayRain) + '</strong>' +
                        '</div>' +
                    '</div>' +
                '</div>'
            );
        }

        if (nodes.briefing.length) {
            nodes.briefing.html('<strong>' + escHtml(currentLine + ' · ' + condition) + '</strong>');
        }

        for (var idx = 1; idx < Math.min((forecast.daily.time || []).length, 6); idx += 1) {
            var day = forecast.daily.time[idx];
            var labels;
            var high;
            var low;
            var wcode;
            var rain = '—';

            if (!day) {
                continue;
            }

            labels = formatDay(day, lang);
            high = Math.round(parseFloat((forecast.daily.temperature_2m_max || [])[idx] || 0));
            low = Math.round(parseFloat((forecast.daily.temperature_2m_min || [])[idx] || 0));
            wcode = parseInt((forecast.daily.weathercode || [])[idx] || 0, 10);

            if (forecast.daily.precipitation_probability_max && typeof forecast.daily.precipitation_probability_max[idx] !== 'undefined') {
                rain = Math.max(0, Math.round(parseFloat(forecast.daily.precipitation_probability_max[idx] || 0))) + '%';
            }

            html += '<div class="noesia-swa-forecast-box">';
            html += '<div class="noesia-swa-forecast-top">';
            html += '<div class="noesia-swa-forecast-day">' + escHtml(labels.weekday) + '</div>';
            html += '<div class="noesia-swa-forecast-date">' + escHtml(labels.date) + '</div>';
            html += '</div>';
            html += '<div class="noesia-swa-forecast-icon">';
            html += '<span class="noesia-swa-forecast-icon-emoji">' + escHtml(weatherEmoji(wcode)) + '</span>';
            html += '<span class="noesia-swa-forecast-condition">' + escHtml(weatherLabel(wcode)) + '</span>';
            html += '</div>';
            html += '<div class="noesia-swa-forecast-temp">';
            html += '<strong class="noesia-swa-forecast-high">' + escHtml(high) + unit + '</strong>';
            html += '<span class="noesia-swa-forecast-low">' + escHtml(low) + unit + '</span>';
            html += '</div>';
            html += '<div class="noesia-swa-forecast-rain">' + escHtml(tLabel('rain') + ' ' + rain) + '</div>';
            html += '</div>';
        }

        if (nodes.forecast.length) {
            nodes.forecast.html(html);
        }

        return true;
    }

    function renderFromCache(settings, lang, preferredTarget) {
        if (!state.cachedForecast || state.cacheKey !== getWeatherCacheKey(settings, lang)) {
            return false;
        }

        return renderWeather(state.cachedForecast, state.cachedSettings || settings, true, preferredTarget);
    }

    function refreshWeather(forceFetch, preferredTarget) {
        var settings = weatherSettings();
        var lang = getLang();
        var preserveExistingUi = hasExistingWeatherData(preferredTarget);
        var query;
        var geocodeUrl;

        if (!hasWeatherUi(preferredTarget)) {
            return Promise.resolve(false);
        }

        if (!settings.city) {
            clearWeatherCache();
            renderUnavailable(preferredTarget);
            return Promise.resolve(false);
        }

        if (!forceFetch && renderFromCache(settings, lang, preferredTarget)) {
            return Promise.resolve(true);
        }

        if (state.busy) {
            return Promise.resolve(renderFromCache(settings, lang, preferredTarget) || preserveExistingUi);
        }

        state.busy = true;
        query = encodeURIComponent(settings.city + (settings.country ? ',' + settings.country : ''));
        geocodeUrl = 'https://geocoding-api.open-meteo.com/v1/search?count=1&language=' + encodeURIComponent(lang) + '&format=json&name=' + query;

        return fetchJson(geocodeUrl, 6000)
            .then(function (geo) {
                if (!geo || !geo.results || !geo.results.length) {
                    return Promise.reject(new Error('no_geo'));
                }

                return fetchJson(buildForecastUrl(geo.results[0].latitude, geo.results[0].longitude, settings), 6000);
            })
            .then(function (data) {
                state.busy = false;
                cacheWeatherData(settings, lang, data);
                return renderWeather(data, settings, true, preferredTarget);
            })
            .catch(function () {
                state.busy = false;

                if (renderFromCache(settings, lang, preferredTarget)) {
                    return true;
                }

                if (preserveExistingUi || hasExistingWeatherData(preferredTarget)) {
                    return true;
                }

                clearWeatherCache();
                renderUnavailable(preferredTarget);
                return false;
            });
    }

    function bindListeners() {
        if (state.listenersBound) {
            return;
        }

        $(document).on('noesiaSwaSectionShown.noesiaSwaWeather', function (event, target) {
            var current = String(target || '');
            if (current === 'dashboard' || current === 'weather') {
                refreshWeather(false, current);
            }
        });

        $(document).on('noesiaSwaOptionsUpdated.noesiaSwaWeather', function () {
            clearWeatherCache();
            refreshWeather(true, $('#noesia-swa-wrapper').attr('data-noesia-swa-section') || 'dashboard');
        });

        $(document).on(
            'change.noesiaSwaWeather',
            'select[name="noesia_swa_options[weather_language]"], select[name="noesia_swa_options[news_language]"], input[name="noesia_swa_options[weather_city]"], input[name="noesia_swa_options[weather_country]"], select[name="noesia_swa_options[weather_units]"]',
            function () {
                clearWeatherCache();
            }
        );

        state.listenersBound = true;
    }

    function boot() {
        if (state.booted) {
            return true;
        }

        bindListeners();
        state.booted = true;
        return true;
    }

    function mount() {
        return boot();
    }

    function init() {
        return boot();
    }

    function onSectionOpen(target) {
        var current = String(target || '');

        if (current && current !== 'dashboard' && current !== 'weather') {
            return false;
        }

        boot();
        refreshWeather(false, current || 'dashboard');
        return true;
    }

    function onShow() {
        return onSectionOpen('weather');
    }

    function show() {
        return onSectionOpen('weather');
    }

    modules.weather = $.extend({}, modules.weather || {}, {
        boot: boot,
        mount: mount,
        init: init,
        onSectionOpen: onSectionOpen,
        onShow: onShow,
        show: show,
        refreshWeather: refreshWeather,
        renderWeather: renderWeather,
        clearCache: clearWeatherCache
    });

    $(boot);
})(window, jQuery);
