import { html, nothing } from 'lit';
import { query, state, property } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { DateTime, Info } from 'luxon';
import FlowRdfElement from '../../internal/flow-ui-rdf-element.mjs';
import { isStringFull, isDefined } from '../../internal/type-checks.mjs';
import { watch } from '../../internal/watch.mjs';
import styles from './calendar.styles.mjs';

var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __decorateClass = (decorators, target, key, kind) => {
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
    if (decorator = decorators[i])
      result = (kind ? decorator(target, key, result) : decorator(result)) || result;
  if (kind && result)
    __defProp(target, key, result);
  return result;
};
class FlowUiCalendar extends FlowRdfElement {
  constructor() {
    super(...arguments);
    this.displayMode = "date";
    this.size = "medium";
  }
  connectedCallback() {
    super.connectedCallback();
    this.handleValueChange();
  }
  disconnectedCallback() {
    super.disconnectedCallback();
  }
  handleValueChange() {
    this._selectedDate = this.makeDate(this.value)?.set({ millisecond: 0 });
    if (this._selectedDate) {
      if (this.displayMode === "date") {
        this._selectedDate = this._selectedDate.startOf("day");
      }
      this._displayDate = this._selectedDate.startOf("month");
    } else {
      this._displayDate = this.makeDate().startOf("month");
    }
  }
  handleTimezoneChange() {
    this.handleValueChange();
    this.updateValue();
  }
  dateClicked(val) {
    const date = this.makeDate(val);
    if (this._selectedDate) {
      this._selectedDate = date.set({
        hour: this._selectedDate.hour,
        minute: this._selectedDate.minute,
        second: this._selectedDate.second
      });
    } else {
      this._selectedDate = date;
    }
    this.updateValue();
  }
  stepDisplayDate(duration) {
    this._displayDate = this._displayDate.plus(duration);
  }
  /**
   * Generate a list of years for the year select dropdown
   */
  yearOptionsForDate(date) {
    const years = [];
    for (let y = date.year + 1; y >= date.year - 6; y--) {
      years.push(y);
    }
    return years;
  }
  makeDate(d) {
    try {
      let date;
      const zone = isStringFull(this.timezone) ? this.timezone : void 0;
      const locale = isStringFull(this.locale) ? this.locale : void 0;
      if (isStringFull(d)) {
        date = DateTime.fromISO(d, { zone, locale });
      } else {
        date = DateTime.local({ zone, locale });
      }
      return date.isValid ? date : void 0;
    } catch (e) {
      console.error(`Invalid date in makeDate: ${d} (${this.timezone})`);
      return void 0;
    }
  }
  updateValue(emitEvents = true) {
    const iso = this._selectedDate?.toISO();
    this.value = isDefined(iso) ? iso : "";
    if (emitEvents) {
      this.emit("flow-ui-input");
      this.emit("flow-ui-change");
    }
  }
  handleHeaderSelectChange(ev) {
    ev.stopPropagation();
    this._displayDate = this.makeDate().set({
      year: +this._yearSelect.value,
      month: +this._monthSelect.value
    }).startOf("month");
  }
  handleTimeChange(ev) {
    ev.stopPropagation();
    if (!this._selectedDate) {
      this._selectedDate = this.makeDate().startOf("day");
    }
    const target = ev.target;
    const unit = target.getAttribute("name");
    this._selectedDate = this._selectedDate.set({ [unit]: +target.value });
    this.updateValue();
  }
  calendarHeaderTemplate() {
    const locale = isStringFull(this.locale) ? this.locale : void 0;
    const monthsShort = Info.months("short", { locale });
    return html`
      <div class="calendar__header">
        <flow-ui-icon-button
          name="chevron-left"
          library="system"
          @click=${() => this.stepDisplayDate({ month: -1 })}
        ></flow-ui-icon-button>
        <flow-ui-select
          inline
          sync="width"
          class="calendar__month-select calendar__header-select"
          value=${this._displayDate.month}
          @flow-ui-change=${this.handleHeaderSelectChange}
        >
          ${monthsShort.map(
      (month, index) => html`
              <flow-ui-option value=${index + 1}>${month}</flow-ui-option>
            `
    )}
        </flow-ui-select>
        <flow-ui-select
          inline
          sync="width"
          class="calendar__year-select calendar__header-select"
          value=${this._displayDate.year}
          @flow-ui-change=${this.handleHeaderSelectChange}
        >
          ${this.yearOptionsForDate(DateTime.now()).map(
      (year) => html`
              <flow-ui-option value=${year}>${year}</flow-ui-option>
            `
    )}
        </flow-ui-select>
        <flow-ui-icon-button
          name="chevron-right"
          library="system"
          @click=${() => this.stepDisplayDate({ month: 1 })}
        ></flow-ui-icon-button>
      </div>
    `;
  }
  dayOfWeekGridTemplate() {
    const locale = isStringFull(this.locale) ? this.locale : void 0;
    const days = Info.weekdays("narrow", { locale });
    return html`
      ${days.map(
      (day) => html`
          <span class="calendar__header-day">${day}</span>
        `
    )}
    `;
  }
  dayGridTemplate() {
    const today = this.makeDate();
    const startOfMonth = this._displayDate.startOf("month");
    const endOfMonth = this._displayDate.endOf("month");
    const calendarStart = startOfMonth.startOf("week");
    const calendarEnd = endOfMonth.endOf("week");
    const daysInMonth = this._displayDate.daysInMonth;
    const days = [];
    for (let date = calendarStart; date < startOfMonth; date = date.plus({ day: 1 })) {
      days.push({
        day: date.toFormat("d"),
        date: date.toISO(),
        isFiller: true,
        today: date === today,
        selected: !!this._selectedDate && date.startOf("day").equals(this._selectedDate.startOf("day"))
      });
    }
    for (let day = 1; day <= daysInMonth; day++) {
      const date = startOfMonth.plus({ days: day - 1 }).startOf("day");
      days.push({
        day: date.toFormat("d"),
        date: date.toISO(),
        isFiller: false,
        today: date.startOf("day").equals(today.startOf("day")),
        selected: !!this._selectedDate && date.startOf("day").equals(this._selectedDate.startOf("day"))
      });
    }
    for (let date = endOfMonth.plus({ day: 1 }).startOf("day"); date < calendarEnd; date = date.plus({ day: 1 })) {
      days.push({
        day: date.toFormat("d"),
        date: date.toISO(),
        isFiller: true,
        today: date.startOf("day").equals(today.startOf("day")),
        selected: !!this._selectedDate && date.startOf("day").equals(this._selectedDate.startOf("day"))
      });
    }
    return html`
      ${days.map(
      (day) => html`
          <button
            class=${classMap({
        calendar__day: true,
        "calendar__day--filler": day.isFiller,
        "calendar__day--today": day.today,
        "calendar__day--selected": day.selected
      })}
            value=${day.date}
            @click=${() => this.dateClicked(day.date)}
          >
            ${day.day}
          </button>
        `
    )}
    `;
  }
  timeTemplate() {
    return html`
      <div class="calendar__time-container">
        <flow-ui-input
          filled
          type="number"
          size=${this.size === "small" ? "small" : "medium"}
          name="hour"
          min="0"
          max="23"
          format="02"
          class="calendar__time-input"
          value=${this._selectedDate ? this._selectedDate.hour : 0}
          @flow-ui-change=${this.handleTimeChange}
        ></flow-ui-input>
        <span class="calendar__time-separator">:</span>
        <flow-ui-input
          filled
          type="number"
          size=${this.size === "small" ? "small" : "medium"}
          name="minute"
          min="0"
          max="59"
          format="02"
          class="calendar__time-input"
          value=${this._selectedDate ? this._selectedDate.minute : 0}
          @flow-ui-change=${this.handleTimeChange}
        ></flow-ui-input>
        <span class="calendar__time-separator">:</span>
        <flow-ui-input
          filled
          type="number"
          size=${this.size === "small" ? "small" : "medium"}
          name="second"
          min="0"
          max="59"
          format="02"
          class="calendar__time-input"
          value=${this._selectedDate ? this._selectedDate.second : 0}
          @flow-ui-change=${this.handleTimeChange}
        ></flow-ui-input>
      </div>
    `;
  }
  render() {
    const showCalendar = this.displayMode === "date" || this.displayMode === "datetime";
    const showTime = this.displayMode === "time" || this.displayMode === "datetime";
    return html`
      <div
        part="base"
        class=${classMap({
      calendar: true,
      "calendar--small": this.size === "small",
      "calendar--medium": this.size === "medium",
      "calendar--large": this.size === "large"
    })}
      >
        ${showCalendar ? html`
              ${this.calendarHeaderTemplate()}
              <div class="calendar__grid">${this.dayOfWeekGridTemplate()} ${this.dayGridTemplate()}</div>
            ` : nothing}
        ${showTime ? this.timeTemplate() : nothing}
      </div>
    `;
  }
}
FlowUiCalendar.styles = styles;
__decorateClass([
  query(".calendar__month-select")
], FlowUiCalendar.prototype, "_monthSelect", 2);
__decorateClass([
  query(".calendar__year-select")
], FlowUiCalendar.prototype, "_yearSelect", 2);
__decorateClass([
  state()
], FlowUiCalendar.prototype, "_displayDate", 2);
__decorateClass([
  state()
], FlowUiCalendar.prototype, "_selectedDate", 2);
__decorateClass([
  property({ reflect: true })
], FlowUiCalendar.prototype, "value", 2);
__decorateClass([
  property({ attribute: "display-mode" })
], FlowUiCalendar.prototype, "displayMode", 2);
__decorateClass([
  property()
], FlowUiCalendar.prototype, "timezone", 2);
__decorateClass([
  property()
], FlowUiCalendar.prototype, "locale", 2);
__decorateClass([
  property()
], FlowUiCalendar.prototype, "size", 2);
__decorateClass([
  watch("value", { waitUntilFirstUpdate: true })
], FlowUiCalendar.prototype, "handleValueChange", 1);
__decorateClass([
  watch(["timezone"], { waitUntilFirstUpdate: true })
], FlowUiCalendar.prototype, "handleTimezoneChange", 1);

export { FlowUiCalendar as default };
