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

    function readCfg() {
        return typeof window.zertiAdminClock === 'object' && window.zertiAdminClock !== null ? window.zertiAdminClock : null;
    }

    function localizeDigits(str) {
        if (typeof window.zertiLocalizeDigits === 'function') {
            return window.zertiLocalizeDigits(str);
        }
        return String(str).replace(/[0-9]/g, function (d) {
            return '۰۱۲۳۴۵۶۷۸۹'.charAt(parseInt(d, 10));
        });
    }

    function toPersianDigits(str) {
        return localizeDigits(str);
    }

    function pad2(n) {
        return n < 10 ? '0' + n : String(n);
    }

    function intdiv(a, b) {
        return Math.floor(a / b);
    }

    /** Same algorithm as zerti_gregorian_to_jalali() in PHP. */
    function gregorianToJalali(gy, gm, gd) {
        var gDaysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
        var jDaysInMonth = [31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 29];

        gy -= 1600;
        gm -= 1;
        gd -= 1;

        var gDayNo = 365 * gy + intdiv(gy + 3, 4) - intdiv(gy + 99, 100) + intdiv(gy + 399, 400);
        var i;
        for (i = 0; i < gm; i += 1) {
            gDayNo += gDaysInMonth[i];
        }
        if (gm > 1 && ((gy % 4 === 0 && gy % 100 !== 0) || gy % 400 === 0)) {
            gDayNo += 1;
        }
        gDayNo += gd;

        var jDayNo = gDayNo - 79;
        var jNp = intdiv(jDayNo, 12053);
        jDayNo %= 12053;

        var jy = 979 + 33 * jNp + 4 * intdiv(jDayNo, 1461);
        jDayNo %= 1461;

        if (jDayNo >= 366) {
            jy += intdiv(jDayNo - 1, 365);
            jDayNo = (jDayNo - 1) % 365;
        }

        var jm = 0;
        while (jm < 11 && jDayNo >= jDaysInMonth[jm]) {
            jDayNo -= jDaysInMonth[jm];
            jm += 1;
        }
        var jd = jDayNo + 1;

        return [jy, jm + 1, jd];
    }

    function partsFromUnix(unixSec, timeZone) {
        var d = new Date(unixSec * 1000);
        var fmt = new Intl.DateTimeFormat('en-GB', {
            timeZone: timeZone,
            year: 'numeric',
            month: 'numeric',
            day: 'numeric',
            hour: '2-digit',
            minute: '2-digit',
            second: '2-digit',
            hour12: false,
        });
        var map = {};
        fmt.formatToParts(d).forEach(function (part) {
            if (part.type !== 'literal') {
                map[part.type] = part.value;
            }
        });
        return {
            gy: parseInt(map.year, 10),
            gm: parseInt(map.month, 10),
            gd: parseInt(map.day, 10),
            hour: map.hour || '00',
            minute: map.minute || '00',
            second: map.second || '00',
        };
    }

    function formatJalaliClockParts(unixSec, timeZone) {
        var p = partsFromUnix(unixSec, timeZone);
        var jalali = gregorianToJalali(p.gy, p.gm, p.gd);
        return {
            date: toPersianDigits(
                pad2(jalali[0]) + '/' + pad2(jalali[1]) + '/' + pad2(jalali[2])
            ),
            time: toPersianDigits(p.hour + ':' + p.minute + ':' + p.second),
        };
    }

    function formatGregorianClockParts(unixSec, timeZone) {
        var p = partsFromUnix(unixSec, timeZone);
        return {
            date: localizeDigits(pad2(p.gy) + '/' + pad2(p.gm) + '/' + pad2(p.gd)),
            time: localizeDigits(p.hour + ':' + p.minute + ':' + p.second),
        };
    }

    function formatHijriClockParts(unixSec, timeZone) {
        var hdLib = window.zertiHijriDate;
        if (!hdLib) {
            return formatGregorianClockParts(unixSec, timeZone);
        }
        var p = partsFromUnix(unixSec, timeZone);
        var hijri = hdLib.gregorianToHijri(p.gy, p.gm, p.gd);
        return {
            date: hdLib.toArabicDigits(
                pad2(hijri[0]) + '/' + pad2(hijri[1]) + '/' + pad2(hijri[2])
            ),
            time: hdLib.toArabicDigits(p.hour + ':' + p.minute + ':' + p.second),
        };
    }

    function formatClockParts(unixSec, timeZone, calendar) {
        if (calendar === 'gregorian') {
            return formatGregorianClockParts(unixSec, timeZone);
        }
        if (calendar === 'hijri') {
            return formatHijriClockParts(unixSec, timeZone);
        }
        return formatJalaliClockParts(unixSec, timeZone);
    }

    function applyClockParts(node, parts) {
        var dateEl = node.querySelector('.zerti-server-clock__date');
        var timeEl = node.querySelector('.zerti-server-clock__time');
        if (dateEl && timeEl) {
            dateEl.textContent = parts.date;
            timeEl.textContent = parts.time;
            return;
        }
        node.textContent = parts.time ? parts.date + ' ' + parts.time : parts.date;
    }

    function findClockNodes() {
        var nodes = document.querySelectorAll('[data-zerti-server-clock="1"], .zerti-server-clock');
        return nodes.length ? nodes : document.querySelectorAll('.zerti-user-server-time .zerti-ltr-nums');
    }

    var clockStarted = false;

    function init() {
        if (clockStarted) {
            return;
        }
        var cfg = readCfg();
        if (!cfg) {
            return;
        }
        var nodes = findClockNodes();
        if (!nodes.length) {
            return;
        }
        clockStarted = true;

        var serverUnix = parseInt(cfg.serverUnix, 10) || 0;
        var timeZone = cfg.timeZone || 'UTC';
        var calendar = cfg.calendar || 'jalali';
        var clientUnixAtLoad = Math.floor(Date.now() / 1000);
        var timerId = null;

        function currentServerUnix() {
            return serverUnix + Math.floor(Date.now() / 1000) - clientUnixAtLoad;
        }

        function tick() {
            try {
                var parts = formatClockParts(currentServerUnix(), timeZone, calendar);
                nodes.forEach(function (node) {
                    applyClockParts(node, parts);
                });
            } catch (err) {
                /* Keep last good label if Intl/timezone fails on a tick. */
            }
        }

        tick();
        timerId = window.setInterval(tick, 1000);

        document.addEventListener('visibilitychange', function () {
            if (document.hidden) {
                if (timerId) {
                    window.clearInterval(timerId);
                    timerId = null;
                }
                return;
            }
            if (!timerId) {
                tick();
                timerId = window.setInterval(tick, 1000);
            }
        });
    }

    function boot() {
        init();
    }

    window.zertiInitServerClock = boot;

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', boot);
    } else {
        boot();
    }
})(window, document);
