(function () {
  "use strict";

  var monthNames = [
    "",
    "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور",
    "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند"
  ];
  var weekNames = ["ش", "ی", "د", "س", "چ", "پ", "ج"];
  var openPicker = null;

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

  function gregorianToJalali(gy, gm, gd) {
    var gdm = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
    var gy2 = gy + (gm > 2 ? 1 : 0);
    var days = 355666 + 365 * gy + div(gy2 + 3, 4) - div(gy2 + 99, 100) +
      div(gy2 + 399, 400) + gd + gdm[gm - 1];
    var jy = -1595 + 33 * div(days, 12053);
    days %= 12053;
    jy += 4 * div(days, 1461);
    days %= 1461;
    if (days > 365) {
      jy += div(days - 1, 365);
      days = (days - 1) % 365;
    }
    if (days < 186) {
      return { year: jy, month: 1 + div(days, 31), day: 1 + (days % 31) };
    }
    return { year: jy, month: 7 + div(days - 186, 30), day: 1 + ((days - 186) % 30) };
  }

  function jalaliToGregorian(jy, jm, jd) {
    jy += 1595;
    var days = -355668 + 365 * jy + div(jy, 33) * 8 + div((jy % 33) + 3, 4) + jd;
    days += jm < 7 ? (jm - 1) * 31 : (jm - 7) * 30 + 186;
    var gy = 400 * div(days, 146097);
    days %= 146097;
    if (days > 36524) {
      gy += 100 * div(days - 1, 36524);
      days = (days - 1) % 36524;
      if (days >= 365) {
        days += 1;
      }
    }
    gy += 4 * div(days, 1461);
    days %= 1461;
    if (days > 365) {
      gy += div(days - 1, 365);
      days = (days - 1) % 365;
    }
    var gd = days + 1;
    var lengths = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    if ((gy % 4 === 0 && gy % 100 !== 0) || gy % 400 === 0) {
      lengths[2] = 29;
    }
    var gm = 1;
    while (gm <= 12 && gd > lengths[gm]) {
      gd -= lengths[gm];
      gm += 1;
    }
    return { year: gy, month: gm, day: gd };
  }

  function todayJalali() {
    var now = new Date();
    return gregorianToJalali(now.getFullYear(), now.getMonth() + 1, now.getDate());
  }

  function daysInMonth(year, month) {
    if (month <= 6) {
      return 31;
    }
    if (month <= 11) {
      return 30;
    }
    var current = jalaliToGregorian(year, 1, 1);
    var next = jalaliToGregorian(year + 1, 1, 1);
    var currentUTC = Date.UTC(current.year, current.month - 1, current.day);
    var nextUTC = Date.UTC(next.year, next.month - 1, next.day);
    return (nextUTC - currentUTC) / 86400000 === 366 ? 30 : 29;
  }

  function parseDate(value) {
    var match = /^\s*(\d{4})-(\d{2})-(\d{2})\s*$/.exec(value);
    if (!match) {
      return null;
    }
    var date = { year: Number(match[1]), month: Number(match[2]), day: Number(match[3]) };
    if (date.year < 1200 || date.month < 1 || date.month > 12 ||
        date.day < 1 || date.day > daysInMonth(date.year, date.month)) {
      return null;
    }
    return date;
  }

  function formatDate(date) {
    return String(date.year).padStart(4, "0") + "-" +
      String(date.month).padStart(2, "0") + "-" +
      String(date.day).padStart(2, "0");
  }

  function sameDate(a, b) {
    return a && b && a.year === b.year && a.month === b.month && a.day === b.day;
  }

  function JalaliPicker(input) {
    this.input = input;
    this.field = input.closest(".jalali-field");
    this.trigger = this.field.querySelector(".jalali-trigger");
    this.selected = parseDate(input.value);
    var initial = this.selected || todayJalali();
    this.year = initial.year;
    this.month = initial.month;
    this.popup = document.createElement("div");
    this.popup.className = "jalali-picker";
    this.popup.hidden = true;
    this.popup.setAttribute("role", "dialog");
    this.popup.setAttribute("aria-label", "Persian date picker");
    this.popup.setAttribute("dir", "rtl");
    this.field.appendChild(this.popup);

    this.trigger.addEventListener("click", this.toggle.bind(this));
    this.input.addEventListener("focus", this.open.bind(this));
    this.input.addEventListener("change", this.sync.bind(this));
    this.input.addEventListener("keydown", this.onKeyDown.bind(this));
  }

  JalaliPicker.prototype.sync = function () {
    var parsed = parseDate(this.input.value);
    this.input.setCustomValidity(this.input.value && !parsed ? "Use a valid Persian date in YYYY-MM-DD format." : "");
    if (parsed) {
      this.selected = parsed;
      this.year = parsed.year;
      this.month = parsed.month;
      if (!this.popup.hidden) {
        this.render();
      }
    }
  };

  JalaliPicker.prototype.onKeyDown = function (event) {
    if (event.key === "Escape") {
      this.close();
      this.input.blur();
    } else if (event.key === "ArrowDown" && this.popup.hidden) {
      event.preventDefault();
      this.open();
    }
  };

  JalaliPicker.prototype.toggle = function () {
    if (this.popup.hidden) {
      this.open();
    } else {
      this.close();
    }
  };

  JalaliPicker.prototype.open = function () {
    if (openPicker && openPicker !== this) {
      openPicker.close();
    }
    this.sync();
    this.popup.hidden = false;
    this.field.classList.add("picker-open");
    this.trigger.setAttribute("aria-expanded", "true");
    openPicker = this;
    this.render();
  };

  JalaliPicker.prototype.close = function () {
    this.popup.hidden = true;
    this.field.classList.remove("picker-open");
    this.trigger.setAttribute("aria-expanded", "false");
    if (openPicker === this) {
      openPicker = null;
    }
  };

  JalaliPicker.prototype.moveMonth = function (amount) {
    this.month += amount;
    if (this.month < 1) {
      this.month = 12;
      this.year -= 1;
    } else if (this.month > 12) {
      this.month = 1;
      this.year += 1;
    }
    this.render();
  };

  JalaliPicker.prototype.choose = function (day) {
    this.selected = { year: this.year, month: this.month, day: day };
    this.input.value = formatDate(this.selected);
    this.input.setCustomValidity("");
    this.input.dispatchEvent(new Event("input", { bubbles: true }));
    this.input.dispatchEvent(new Event("change", { bubbles: true }));

    if (this.input.dataset.jalaliRole === "start") {
      var end = document.querySelector('[data-jalali-role="end"]');
      var endDate = end && parseDate(end.value);
      if (end && (!endDate || end.value < this.input.value)) {
        end.value = this.input.value;
        end.dispatchEvent(new Event("change", { bubbles: true }));
      }
    }
    this.close();
    this.input.focus();
  };

  JalaliPicker.prototype.render = function () {
    var picker = this;
    var today = todayJalali();
    var selected = parseDate(this.input.value) || this.selected;
    var firstGregorian = jalaliToGregorian(this.year, this.month, 1);
    var offset = (new Date(firstGregorian.year, firstGregorian.month - 1, firstGregorian.day).getDay() + 1) % 7;
    var count = daysInMonth(this.year, this.month);

    this.popup.innerHTML =
      '<div class="jalali-picker-head">' +
        '<button type="button" data-next aria-label="ماه بعد">‹</button>' +
        '<strong>' + monthNames[this.month] + ' <span>' + this.year + '</span></strong>' +
        '<button type="button" data-prev aria-label="ماه قبل">›</button>' +
      '</div>' +
      '<div class="jalali-weekdays">' +
        weekNames.map(function (name) { return "<span>" + name + "</span>"; }).join("") +
      '</div>' +
      '<div class="jalali-days"></div>' +
      '<button type="button" class="jalali-today">امروز · ' + formatDate(today) + "</button>";

    var days = this.popup.querySelector(".jalali-days");
    for (var blank = 0; blank < offset; blank += 1) {
      var spacer = document.createElement("span");
      spacer.className = "jalali-blank";
      days.appendChild(spacer);
    }
    for (var day = 1; day <= count; day += 1) {
      var date = { year: this.year, month: this.month, day: day };
      var button = document.createElement("button");
      button.type = "button";
      button.textContent = String(day);
      button.dataset.day = String(day);
      if ((offset + day - 1) % 7 === 6) {
        button.classList.add("friday");
      }
      if (sameDate(date, today)) {
        button.classList.add("today");
      }
      if (sameDate(date, selected)) {
        button.classList.add("selected");
        button.setAttribute("aria-current", "date");
      }
      button.addEventListener("click", function () {
        picker.choose(Number(this.dataset.day));
      });
      days.appendChild(button);
    }

    this.popup.querySelector("[data-prev]").addEventListener("click", function () { picker.moveMonth(-1); });
    this.popup.querySelector("[data-next]").addEventListener("click", function () { picker.moveMonth(1); });
    this.popup.querySelector(".jalali-today").addEventListener("click", function () {
      picker.year = today.year;
      picker.month = today.month;
      picker.choose(today.day);
    });
  };

  document.addEventListener("DOMContentLoaded", function () {
    document.querySelectorAll("[data-jalali-picker]").forEach(function (input) {
      new JalaliPicker(input);
    });
    document.addEventListener("pointerdown", function (event) {
      if (openPicker && !openPicker.field.contains(event.target)) {
        openPicker.close();
      }
    });
  });
})();
