/**
 * Neo Jalali DatePicker
 * یک DatePicker کاملاً سفارشی برای تقویم شمسی
 * نسخه: 1.0.0
 */

(function (global) {
    'use strict';

    // ============ توابع کمکی ============

    function toEnglishDigits(str) {
        var map = {
            '۰': '0', '۱': '1', '۲': '2', '۳': '3', '۴': '4',
            '۵': '5', '۶': '6', '۷': '7', '۸': '8', '۹': '9',
            '٠': '0', '١': '1', '٢': '2', '٣': '3', '٤': '4',
            '٥': '5', '٦': '6', '٧': '7', '٨': '8', '٩': '9'
        };
        return String(str || '').replace(/[۰-۹٠-٩]/g, function (d) { return map[d] || d; });
    }

    function isLikelyJalaliYear(year) {
        return year >= 1300 && year <= 1500;
    }

    function pad2(value) {
        return String(value).padStart(2, '0');
    }

    function extractDateParts(raw) {
        var clean = toEnglishDigits(raw || '').trim();
        if (!clean) return null;
        clean = clean.replace(/\./g, '/').replace(/-/g, '/');
        var parts = clean.split('/');
        if (parts.length !== 3) return null;
        var year = parseInt(parts[0], 10);
        var month = parseInt(parts[1], 10);
        var day = parseInt(parts[2], 10);
        if (!year || !month || !day) return null;
        return {
            year: year,
            month: month,
            day: day,
            normalized: String(year).padStart(4, '0') + '/' + pad2(month) + '/' + pad2(day)
        };
    }

    // ============ تبدیل تاریخ ============

    function gregorianToJalali(gy, gm, gd) {
        var g_d_m = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
        var isLeap = (gy % 4 === 0 && gy % 100 !== 0) || (gy % 400 === 0);
        if (isLeap) g_d_m[2] = 29;
        var days = 355666 + (365 * gy) + Math.floor((gy + 3) / 4) - Math.floor((gy + 99) / 100) + Math.floor((gy + 399) / 400) + gd;
        for (var i = 0; i < gm; i++) {
            days += g_d_m[i];
        }
        var jy = -1595 + 33 * Math.floor(days / 12053);
        days %= 12053;
        jy += 4 * Math.floor(days / 1461);
        days %= 1461;
        if (days > 365) {
            jy += Math.floor((days - 1) / 365);
            days = (days - 1) % 365;
        }
        var jm, jd;
        if (days < 186) {
            jm = 1 + Math.floor(days / 31);
            jd = 1 + (days % 31);
        } else {
            jm = 7 + Math.floor((days - 186) / 30);
            jd = 1 + ((days - 186) % 30);
        }
        return [jy, jm, jd];
    }

    function jalaliToGregorian(jy, jm, jd) {
        var j_month_days = [31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29];
        jy = jy - 979;
        jm = jm - 1;
        jd = jd - 1;

        var j_day_no = 365 * jy + Math.floor(jy / 33) * 8 + Math.floor(((jy % 33) + 3) / 4);
        for (var i = 0; i < jm; ++i) {
            j_day_no += j_month_days[i];
        }
        j_day_no += jd;

        var g_day_no = j_day_no + 79;
        var gy = 1600 + 400 * Math.floor(g_day_no / 146097);
        g_day_no = g_day_no % 146097;

        var leap = true;
        if (g_day_no >= 36525) {
            g_day_no--;
            gy += 100 * Math.floor(g_day_no / 36524);
            g_day_no = g_day_no % 36524;

            if (g_day_no >= 365) {
                g_day_no++;
            } else {
                leap = false;
            }
        }

        gy += 4 * Math.floor(g_day_no / 1461);
        g_day_no %= 1461;

        if (g_day_no >= 366) {
            leap = false;
            g_day_no--;
            gy += Math.floor(g_day_no / 365);
            g_day_no = g_day_no % 365;
        }

        var g_month_days = [31, (leap ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
        var gm = 0;
        for (gm = 0; gm < 12 && g_day_no >= g_month_days[gm]; gm++) {
            g_day_no -= g_month_days[gm];
        }
        var gd = g_day_no + 1;
        return [gy, gm + 1, gd];
    }

    function formatJalaliDateFromGregorian(dateObj) {
        if (!dateObj) return '';
        var gy = dateObj.getFullYear();
        var gm = dateObj.getMonth() + 1;
        var gd = dateObj.getDate();
        var parts = gregorianToJalali(gy, gm, gd);
        var jy = String(parts[0]).padStart(4, '0');
        var jm = String(parts[1]).padStart(2, '0');
        var jd = String(parts[2]).padStart(2, '0');
        return jy + '/' + jm + '/' + jd;
    }

    // ============ تقویم شمسی ============

    var jalaliMonthNames = [
        'فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور',
        'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند'
    ];

    function jalaliLeap(jy) {
        var breaks = [-61, 9, 38, 199, 426, 686, 756, 818, 1111, 1181, 1210, 1635, 2060, 2097, 2192, 2262, 2324, 2394, 2456, 3178];
        var bl = breaks.length;
        var jp = breaks[0];
        if (jy < jp || jy >= breaks[bl - 1]) return false;
        var jump = 0;
        var leapJ = -14;
        for (var i = 1; i < bl; i++) {
            var jm = breaks[i];
            jump = jm - jp;
            if (jy < jm) break;
            leapJ = leapJ + Math.floor(jump / 33) * 8 + Math.floor((jump % 33) / 4);
            jp = jm;
        }
        var n = jy - jp;
        leapJ = leapJ + Math.floor(n / 33) * 8 + Math.floor(((n % 33) + 3) / 4);
        if (jump % 33 === 4 && jump - n === 4) leapJ += 1;
        var leap = (((n + 1) % 33) - 1) % 4;
        if (leap === -1) leap = 4;
        return leap === 0;
    }

    function getJalaliMonthDays(jy, jm) {
        if (jm <= 6) return 31;
        if (jm <= 11) return 30;
        return jalaliLeap(jy) ? 30 : 29;
    }

    function getTodayJalali() {
        var now = new Date();
        var parts = gregorianToJalali(now.getFullYear(), now.getMonth() + 1, now.getDate());
        return { year: parts[0], month: parts[1], day: parts[2] };
    }

    function parseInputToJalali(value) {
        if (value && typeof value.getFullYear === 'function' && typeof value.getMonth === 'function' && typeof value.getDate === 'function' && !isNaN(value.getTime())) {
            var dateParts = gregorianToJalali(value.getFullYear(), value.getMonth() + 1, value.getDate());
            return { year: dateParts[0], month: dateParts[1], day: dateParts[2] };
        }
        var raw = String(value || '').trim();
        if (!raw) return null;
        var parts = extractDateParts(raw);
        if (parts) {
            if (isLikelyJalaliYear(parts.year)) {
                return { year: parts.year, month: parts.month, day: parts.day };
            }
            if (parts.year >= 1900) {
                var converted = gregorianToJalali(parts.year, parts.month, parts.day);
                return { year: converted[0], month: converted[1], day: converted[2] };
            }
        }
        if (/^\d{4}-\d{2}-\d{2}/.test(raw)) {
            var gy = parseInt(raw.substring(0, 4), 10);
            var gm = parseInt(raw.substring(5, 7), 10);
            var gd = parseInt(raw.substring(8, 10), 10);
            var conv = gregorianToJalali(gy, gm, gd);
            return { year: conv[0], month: conv[1], day: conv[2] };
        }
        return null;
    }

    function compareJalali(a, b) {
        if (!a || !b) return 0;
        if (a.year !== b.year) return a.year - b.year;
        if (a.month !== b.month) return a.month - b.month;
        return a.day - b.day;
    }

    function formatJalaliValue(jy, jm, jd) {
        return String(jy).padStart(4, '0') + '/' + pad2(jm) + '/' + pad2(jd);
    }

    function normalizeJalaliInputValue(value) {
        var raw = String(value || '').trim();
        if (!raw) return '';
        var parts = extractDateParts(raw);
        if (parts) {
            if (isLikelyJalaliYear(parts.year)) {
                return parts.normalized;
            }
            if (parts.year >= 1900) {
                var dt = new Date(parts.year, parts.month - 1, parts.day);
                if (!isNaN(dt.getTime())) return formatJalaliDateFromGregorian(dt);
            }
        }
        if (/^\d{4}-\d{2}-\d{2}/.test(raw)) {
            var dt2 = new Date(raw.substring(0, 10));
            if (!isNaN(dt2.getTime())) return formatJalaliDateFromGregorian(dt2);
        }
        return raw;
    }

    // ============ رندر Picker ============

    function positionPicker(picker, input) {
        var rect = input.getBoundingClientRect();
        var pickerHeight = picker.offsetHeight || 300;
        var pickerWidth = picker.offsetWidth || 260;
        var viewportHeight = window.innerHeight;
        var viewportWidth = window.innerWidth;
        var spaceBelow = viewportHeight - rect.bottom;
        var spaceAbove = rect.top;
        var margin = 8;
        var top;
        var left;

        // اگر فضای کافی در پایین نیست، در بالا نمایش بده
        if (spaceBelow < pickerHeight && spaceAbove > pickerHeight) {
            top = window.scrollY + rect.top - pickerHeight - 6;
        } else {
            top = window.scrollY + rect.bottom + 6;
        }

        left = window.scrollX + rect.right - pickerWidth;
        var minLeft = window.scrollX + margin;
        var maxLeft = window.scrollX + viewportWidth - pickerWidth - margin;
        if (left < minLeft) left = minLeft;
        if (left > maxLeft) left = maxLeft;

        picker.style.top = top + 'px';
        picker.style.left = left + 'px';
    }

    function renderJalaliPicker(picker, state) {
        var jy = state.year;
        var jm = state.month;
        var jd = state.day;
        var monthName = jalaliMonthNames[jm - 1] || '';
        var title = monthName + ' ' + jy;
        var firstG = jalaliToGregorian(jy, jm, 1);
        var jsDay = new Date(firstG[0], firstG[1] - 1, firstG[2]).getDay();
        var offset = (jsDay + 1) % 7;
        var daysInMonth = getJalaliMonthDays(jy, jm);
        var today = getTodayJalali();
        
        var html = '';
        html += '<div class="neo-jdp-header">';
        html += '<button type="button" class="neo-jdp-nav" data-action="prev">‹</button>';
        html += '<div class="neo-jdp-title">' + title + '</div>';
        html += '<button type="button" class="neo-jdp-nav" data-action="next">›</button>';
        html += '</div>';
        html += '<div class="neo-jdp-week">';
        var week = ['ش', 'ی', 'د', 'س', 'چ', 'پ', 'ج'];
        for (var w = 0; w < week.length; w++) {
            html += '<div class="neo-jdp-weekday">' + week[w] + '</div>';
        }
        html += '</div>';
        html += '<div class="neo-jdp-grid">';
        for (var i = 0; i < offset; i++) {
            html += '<div class="neo-jdp-empty"></div>';
        }
        for (var d = 1; d <= daysInMonth; d++) {
            var candidate = { year: jy, month: jm, day: d };
            var isDisabled = (state.minDate && compareJalali(candidate, state.minDate) < 0)
                || (state.maxDate && compareJalali(candidate, state.maxDate) > 0);
            var classes = 'neo-jdp-day';
            if (d === jd) classes += ' selected';
            if (d === today.day && jm === today.month && jy === today.year) classes += ' today';
            if (isDisabled) classes += ' disabled';
            html += '<button type="button" class="' + classes + '" data-day="' + d + '"' + (isDisabled ? ' disabled aria-disabled="true"' : '') + '>' + d + '</button>';
        }
        html += '</div>';
        
        picker.innerHTML = html;
        
        function handlePickerAction(ev) {
            var target = ev.target;
            if (!target) return;

            // ممکن است متنِ داخل دکمه‌ها توسط فارسی‌سازِ ارقام در <span> پیچیده شده باشد،
            // پس هدفِ رویداد آن span است نه خود دکمه. با closest به نزدیک‌ترین دکمه می‌رسیم.
            var navBtn = target.closest ? target.closest('.neo-jdp-nav') : null;
            var dayBtn = target.closest ? target.closest('.neo-jdp-day') : null;

            // کلیک روی دکمه‌های navigation
            if (navBtn) {
                ev.preventDefault();
                ev.stopPropagation();

                var action = navBtn.getAttribute('data-action');
                if (action === 'prev') {
                    var month = state.month - 1;
                    var year = state.year;
                    if (month < 1) {
                        month = 12;
                        year -= 1;
                    }
                    state.year = year;
                    state.month = month;
                    state.day = Math.min(state.day, getJalaliMonthDays(year, month));
                    renderJalaliPicker(picker, state);
                } else if (action === 'next') {
                    var month = state.month + 1;
                    var year = state.year;
                    if (month > 12) {
                        month = 1;
                        year += 1;
                    }
                    state.year = year;
                    state.month = month;
                    state.day = Math.min(state.day, getJalaliMonthDays(year, month));
                    renderJalaliPicker(picker, state);
                }
            }

            // کلیک روی روزها
            else if (dayBtn) {
                ev.preventDefault();
                ev.stopPropagation();
                if (dayBtn.disabled || dayBtn.classList.contains('disabled')) {
                    return;
                }

                var day = parseInt(dayBtn.getAttribute('data-day'), 10);
                state.day = day;
                var formatted = formatJalaliValue(state.year, state.month, state.day);
                state.input.value = formatted;
                
                // فراخوانی callback اگر وجود داشت
                if (typeof state.onSelect === 'function') {
                    state.onSelect(formatted);
                }
                
                // ارسال events
                state.input.dispatchEvent(new Event('input', { bubbles: true }));
                state.input.dispatchEvent(new Event('change', { bubbles: true }));
                
                // بستن picker
                state.close();
            }
        }

        if (global.PointerEvent) {
            picker.onpointerdown = handlePickerAction;
            picker.onmousedown = function(ev) { ev.stopPropagation(); };
            picker.onclick = function(ev) { ev.preventDefault(); ev.stopPropagation(); };
        } else {
            picker.onmousedown = handlePickerAction;
            picker.onclick = function(ev) { ev.preventDefault(); ev.stopPropagation(); };
        };
    }

    // ============ تابع اصلی Initialize ============

    // رجیستری همه‌ی Pickerها تا در هر لحظه فقط یکی باز باشد
    var allStates = [];

    function closeOtherPickers(except) {
        for (var i = 0; i < allStates.length; i++) {
            var s = allStates[i];
            if (s !== except && s.picker && s.picker.style.display !== 'none') {
                s.close();
            }
        }
    }

    function initJalaliPicker(input, onSelect, options) {
        if (!input) return;
        options = options || {};
        input.removeAttribute('readonly');
        
        var normalizedValue = normalizeJalaliInputValue(input.value);
        if (normalizedValue) {
            input.value = normalizedValue;
        }
        
        // اگر قبلاً initialize شده، فقط callback را update کن
        if (input._neoJdp) {
            if (onSelect) input._neoJdp.onSelect = onSelect;
            input._neoJdp.minDate = parseInputToJalali(options.minDate || options.minDateGregorian || options.minDateJalali || null);
            input._neoJdp.maxDate = parseInputToJalali(options.maxDate || options.maxDateGregorian || options.maxDateJalali || null);
            return input._neoJdp;
        }
        
        var picker = document.createElement('div');
        picker.className = 'neo-jdp';
        picker.style.display = 'none';
        document.body.appendChild(picker);
        
        var state = {
            input: input,
            picker: picker,
            onSelect: onSelect,
            minDate: parseInputToJalali(options.minDate || options.minDateGregorian || options.minDateJalali || null),
            maxDate: parseInputToJalali(options.maxDate || options.maxDateGregorian || options.maxDateJalali || null),
            year: 0,
            month: 0,
            day: 0,
            close: function () {
                picker.style.display = 'none';
            }
        };
        
        function openPicker() {
            // قبل از باز کردن این Picker، بقیه‌ی Pickerهای باز را ببند تا روی هم نیفتند
            closeOtherPickers(state);
            var parsed = parseInputToJalali(input.value) || getTodayJalali();
            state.year = parsed.year;
            state.month = parsed.month;
            state.day = parsed.day;
            renderJalaliPicker(picker, state);
            picker.style.display = 'block';
            positionPicker(picker, input);
        }
        
        function handleInputClick(ev) {
            ev.stopPropagation();
            openPicker();
        }

        function handleDocumentClick(ev) {
            if (ev.target === input || picker.contains(ev.target)) return;
            state.close();
        }

        input.addEventListener('focus', openPicker);
        input.addEventListener('click', handleInputClick);
        document.addEventListener('click', handleDocumentClick);

        state.destroy = function () {
            input.removeEventListener('focus', openPicker);
            input.removeEventListener('click', handleInputClick);
            document.removeEventListener('click', handleDocumentClick);
            if (picker.parentNode) {
                picker.parentNode.removeChild(picker);
            }
            var idx = allStates.indexOf(state);
            if (idx !== -1) allStates.splice(idx, 1);
            delete input._neoJdp;
        };

        input._neoJdp = state;
        allStates.push(state);
        return state;
    }

    // ============ Export به Global ============

    global.NeoJalaliDatePicker = {
        init: initJalaliPicker,
        gregorianToJalali: gregorianToJalali,
        jalaliToGregorian: jalaliToGregorian,
        formatJalaliDateFromGregorian: formatJalaliDateFromGregorian,
        getTodayJalali: getTodayJalali,
        version: '1.0.0'
    };

    // برای سازگاری با کدهای قبلی
    global.initJalaliPicker = initJalaliPicker;

})(window);
