/* COPYRIGHT Esri - https://js.arcgis.com/5.1/LICENSE.txt */
import { c as customElement } from "../../chunks/runtime.js";
import { repeat } from "lit/directives/repeat.js";
import { keyed } from "lit/directives/keyed.js";
import { css, nothing, html } from "lit";
import { LitElement, createEvent, stringOrBoolean, safeClassMap } from "@arcgis/lumina";
import { createRef, ref } from "lit/directives/ref.js";
import { c as connectLabel, d as disconnectLabel } from "../../chunks/label.js";
import { u as useT9n } from "../../chunks/useT9n.js";
import { S as SLOTS$1 } from "../../chunks/resources7.js";
import { u as useSetFocus } from "../../chunks/useSetFocus.js";
import { u as useInteractive } from "../../chunks/useInteractive.js";
import { u as useForm } from "../../chunks/useForm.js";
import { g as getDateTimeFormat } from "../../chunks/locale.js";
const CSS = {
  offset: "offset"
};
const SLOTS = {
  labelContent: "label-content"
};
const hourToMinutes = 60;
function timeZoneOffsetToDecimal(shortOffsetTimeZoneName) {
  const minusSign = "−";
  const hyphen = "-";
  return shortOffsetTimeZoneName.replace(":15", ".25").replace(":30", ".5").replace(":45", ".75").replace(minusSign, hyphen);
}
function toOffsetValue(timeZoneName, referenceDateInMs) {
  const offset = getTimeZoneShortOffset(timeZoneName, "en-US", referenceDateInMs).replace("GMT", "");
  if (offset === "") {
    return 0;
  }
  return Number(timeZoneOffsetToDecimal(offset)) * hourToMinutes;
}
function getUserTimeZoneOffset() {
  const localDate = /* @__PURE__ */ new Date();
  return localDate.getTimezoneOffset() * -1;
}
function getUserTimeZoneName() {
  const dateFormatter = new Intl.DateTimeFormat();
  return dateFormatter.resolvedOptions().timeZone;
}
async function getNormalizer(mode) {
  if (mode === "offset") {
    return (timeZone) => timeZone;
  }
  const { normalize } = await import("timezone-groups/utils/time-zones");
  return normalize;
}
async function createTimeZoneItems(locale, messages, mode, referenceDate, standardTime) {
  if (mode === "name") {
    const { groupByName } = await import("timezone-groups/groupByName");
    const groups2 = await groupByName();
    return groups2.map(({ label: timeZone }) => {
      const label = timeZone;
      const value = timeZone;
      return {
        label,
        value,
        metadata: {
          filterValue: timeZone
        }
      };
    }).filter((group) => !!group).sort();
  }
  const effectiveLocale = standardTime === "user" ? locale : (
    // we use locales that will always yield a short offset that matches `standardTime`
    standardTime === "utc" ? "fr" : "en-GB"
  );
  const referenceDateInMs = referenceDate.getTime();
  if (mode === "region") {
    const [{ groupByRegion }, { getCountry, global: globalLabel }] = await Promise.all([
      import("timezone-groups/groupByRegion"),
      import("timezone-groups/utils/region")
    ]);
    const groups2 = await groupByRegion();
    return groups2.map(({ label: region, tzs }) => {
      tzs.sort((timeZoneA, timeZoneB) => {
        const labeledTimeZoneA = getTimeZoneLabel(timeZoneA, messages);
        const labeledTimeZoneB = getTimeZoneLabel(timeZoneB, messages);
        const gmtTimeZoneString = "Etc/GMT";
        if (timeZoneA.startsWith(gmtTimeZoneString) && timeZoneB.startsWith(gmtTimeZoneString)) {
          const offsetStringA = timeZoneA.substring(gmtTimeZoneString.length);
          const offsetStringB = timeZoneB.substring(gmtTimeZoneString.length);
          const offsetA = offsetStringA === "" ? 0 : parseInt(offsetStringA);
          const offsetB = offsetStringB === "" ? 0 : parseInt(offsetStringB);
          return offsetB - offsetA;
        }
        return labeledTimeZoneA.localeCompare(labeledTimeZoneB);
      });
      return {
        label: getMessageOrKeyFallback(messages, region),
        items: tzs.map((timeZone) => {
          const decimalOffset = timeZoneOffsetToDecimal(
            getTimeZoneShortOffset(timeZone, effectiveLocale, referenceDateInMs)
          );
          const label = getTimeZoneLabel(timeZone, messages);
          const filterValue = region === globalLabel ? (
            // we rely on the label for search since GMT items have their signs inverted (see https://en.wikipedia.org/wiki/Tz_database#Area)
            // in addition to the label we also add "Global" and "Etc" to allow searching for these items
            `${getTimeZoneLabel(globalLabel, messages)} Etc`
          ) : toUserFriendlyName(timeZone);
          const countryCode = getCountry(timeZone);
          const country = getMessageOrKeyFallback(messages, countryCode);
          return {
            label,
            value: timeZone,
            metadata: {
              country: country === label ? void 0 : country,
              filterValue,
              offset: decimalOffset
            }
          };
        })
      };
    }).sort(
      (groupA, groupB) => groupA.label === globalLabel ? -1 : groupB.label === globalLabel ? 1 : groupA.label.localeCompare(groupB.label)
    );
  }
  const [{ groupByOffset }, { DateEngine }] = await Promise.all([
    import("timezone-groups/groupByOffset"),
    import("timezone-groups/groupByOffset/strategy/native")
  ]);
  const groups = await groupByOffset({
    dateEngine: new DateEngine(),
    groupDateRange: 1,
    startDate: new Date(referenceDateInMs).toISOString()
  });
  const listFormatter = new Intl.ListFormat(locale, { style: "long", type: "conjunction" });
  const offsetTimeZoneNameBlockList = ["Factory", "Etc/UTC"];
  groups.forEach((group) => {
    const indexOffsets = [];
    let removedSoFar = 0;
    group.tzs.forEach((tz, index) => {
      if (offsetTimeZoneNameBlockList.includes(tz)) {
        removedSoFar++;
      }
      indexOffsets[index] = removedSoFar;
    });
    group.tzs = group.tzs.filter((tz) => !offsetTimeZoneNameBlockList.includes(tz));
    group.labelTzIdx = group.labelTzIdx.map((index) => index - indexOffsets[index]).filter((index) => index >= 0 && index < group.tzs.length);
  });
  return groups.map(({ labelTzIdx, tzs }) => {
    const groupRepTz = tzs[0];
    const decimalOffset = timeZoneOffsetToDecimal(
      getTimeZoneShortOffset(groupRepTz, effectiveLocale, referenceDateInMs)
    );
    const value = toOffsetValue(groupRepTz, referenceDateInMs);
    const tzLabels = labelTzIdx.map((index) => getTimeZoneLabel(tzs[index], messages));
    const label = createTimeZoneOffsetLabel(messages, decimalOffset, listFormatter.format(tzLabels));
    return {
      label,
      value,
      metadata: {
        filterValue: tzs.map((tz) => toUserFriendlyName(tz))
      }
    };
  }).filter((group) => !!group).sort((groupA, groupB) => groupA.value - groupB.value);
}
function getTimeZoneLabel(timeZone, messages) {
  return messages[timeZone] || getCity(timeZone);
}
function getSelectedRegionTimeZoneLabel(city, country, messages) {
  const template = messages.timeZoneRegionLabel;
  return template.replace("{city}", city).replace("{country}", getMessageOrKeyFallback(messages, country));
}
function getMessageOrKeyFallback(messages, key) {
  return messages[key] || key;
}
function getCity(timeZone) {
  return timeZone.split("/").pop();
}
function toUserFriendlyName(timeZoneName) {
  return timeZoneName.replace(/_/g, " ");
}
function createTimeZoneOffsetLabel(messages, offsetLabel, groupLabel) {
  return messages.timeZoneLabel.replace("{offset}", offsetLabel).replace("{cities}", groupLabel);
}
function getTimeZoneShortOffset(timeZone, locale, referenceDateInMs = Date.now()) {
  if (timeZone === "Factory") {
    timeZone = "Etc/GMT";
  }
  const dateTimeFormat = getDateTimeFormat(locale, { timeZone, timeZoneName: "shortOffset" });
  const parts = dateTimeFormat.formatToParts(referenceDateInMs);
  return parts.find(({ type }) => type === "timeZoneName").value;
}
function hasGroups(items) {
  return items[0].items !== void 0;
}
function flattenTimeZoneItems(timeZoneItems) {
  return hasGroups(timeZoneItems) ? timeZoneItems.flatMap((item) => item.items) : timeZoneItems;
}
function findTimeZoneItemByProp(timeZoneItems, prop, valueToMatch) {
  return valueToMatch == null ? null : flattenTimeZoneItems(timeZoneItems).find(
    (item) => (
      // intentional == to match string to number
      item[prop] == valueToMatch
    )
  );
}
const styles = css`:host([disabled]){cursor:default;-webkit-user-select:none;user-select:none;opacity:var(--calcite-opacity-disabled)}:host([disabled]) *,:host([disabled]) ::slotted(*){pointer-events:none}:host{display:block}.offset{white-space:nowrap}:host([hidden]){display:none}[hidden]{display:none}:host([disabled]) ::slotted([calcite-hydrated][disabled]),:host([disabled]) [calcite-hydrated][disabled]{opacity:1}.interaction-container{display:contents}::slotted(input[slot=hidden-form-input]){margin:0!important;opacity:0!important;outline:none!important;padding:0!important;position:absolute!important;inset:0!important;transform:none!important;-webkit-appearance:none!important;z-index:-1!important}`;
class InputTimeZone extends LitElement {
  constructor() {
    super(...arguments);
    this.comboboxRef = createRef();
    this.formSupport = useForm({
      inputType: "text"
    })(this);
    this.messages = useT9n({ blocking: true });
    this.focusSetter = useSetFocus()(this);
    this.interactiveContainer = useInteractive(this);
    this.#valueUpdateContext = null;
    this.clearable = false;
    this.disabled = false;
    this.maxItems = 0;
    this.mode = "offset";
    this.offsetStyle = "user";
    this.open = false;
    this.overlayPositioning = "absolute";
    this.readOnly = false;
    this.required = false;
    this.scale = "m";
    this.status = "idle";
    this.topLayerDisabled = false;
    this.calciteInputTimeZoneBeforeClose = createEvent({ cancelable: false });
    this.calciteInputTimeZoneBeforeOpen = createEvent({ cancelable: false });
    this.calciteInputTimeZoneChange = createEvent({ cancelable: false });
    this.calciteInputTimeZoneClose = createEvent({ cancelable: false });
    this.calciteInputTimeZoneOpen = createEvent({ cancelable: false });
  }
  static {
    this.properties = { selectedTimeZoneItem: [16, {}, { state: true }], timeZoneItems: [16, {}, { state: true }], clearable: [7, {}, { reflect: true, type: Boolean }], disabled: [7, {}, { reflect: true, type: Boolean }], form: [3, {}, { reflect: true }], labelText: 1, maxItems: [11, {}, { reflect: true, type: Number }], messageOverrides: [0, {}, { attribute: false }], mode: [3, {}, { reflect: true }], name: [3, {}, { reflect: true }], offsetStyle: [3, {}, { reflect: true }], open: [7, {}, { reflect: true, type: Boolean }], overlayPositioning: [3, {}, { reflect: true }], readOnly: [7, {}, { reflect: true, type: Boolean }], referenceDate: 1, required: [7, {}, { reflect: true, type: Boolean }], scale: [3, {}, { reflect: true }], status: [3, {}, { reflect: true }], topLayerDisabled: [7, {}, { reflect: true, type: Boolean }], validationIcon: [3, { converter: stringOrBoolean, type: String }, { reflect: true }], validationMessage: 1, validity: [32, {}, { readOnly: true, attribute: false }], value: 1 };
  }
  static {
    this.formAssociated = true;
  }
  static {
    this.shadowRootOptions = { mode: "open", delegatesFocus: true };
  }
  static {
    this.styles = styles;
  }
  #valueUpdateContext;
  get value() {
    return this._value;
  }
  set value(value) {
    this.#valueUpdateContext = "internal";
    this._value = value;
  }
  async setFocus(options) {
    return this.focusSetter(() => this.comboboxRef.value, options);
  }
  connectedCallback() {
    super.connectedCallback();
    connectLabel(this);
  }
  async load() {
    this.normalizer = await getNormalizer(this.mode);
    await this.updateTimeZoneItems();
    const initialValue = this.value;
    const normalized = this.normalizeValue(initialValue);
    this.value = normalized || (initialValue === "" ? normalized : void 0);
    this.updateTimeZoneSelection();
    const selectedValue = this.selectedTimeZoneItem ? `${this.selectedTimeZoneItem.value}` : "";
    this.value = selectedValue;
  }
  willUpdate(changes) {
    if (changes.has("value") && this.hasUpdated) {
      this.handleValueChange(this.value, changes.get("value"));
    }
    if (changes.has("messages") || changes.has("mode") && (this.hasUpdated || this.mode !== "offset") || changes.has("referenceDate")) {
      this.handleTimeZoneItemPropsChange();
    }
    if (changes.has("open") && (this.hasUpdated || this.open !== false)) {
      this.openChanged();
    }
  }
  loaded() {
    this.openChanged();
  }
  disconnectedCallback() {
    super.disconnectedCallback();
    disconnectLabel(this);
  }
  async handleTimeZoneItemPropsChange() {
    if (!this.timeZoneItems || !this.hasUpdated) {
      return;
    }
    await this.updateTimeZoneItems();
    this.updateTimeZoneSelection();
  }
  openChanged() {
    if (this.comboboxRef.value) {
      this.comboboxRef.value.open = this.open;
    }
  }
  async handleValueChange(value, oldValue) {
    const userUpdated = this.#valueUpdateContext === "user";
    this.#valueUpdateContext = null;
    if (userUpdated) {
      return;
    }
    const normalized = this.normalizeValue(value);
    if (!normalized) {
      if (this.clearable) {
        this._value = normalized;
        this.selectedTimeZoneItem = null;
        return;
      }
      this._value = oldValue;
      this.selectedTimeZoneItem = this.findTimeZoneItem(oldValue);
      return;
    }
    const timeZoneItem = this.findTimeZoneItem(normalized);
    if (!timeZoneItem) {
      this._value = oldValue;
      return;
    }
    this._value = normalized;
    this.selectedTimeZoneItem = timeZoneItem;
  }
  onLabelClick() {
    this.setFocus();
  }
  overrideSelectedLabelForRegion(open) {
    if (this.mode !== "region" || !this.selectedTimeZoneItem) {
      return;
    }
    this.comboboxRef.value.selectedItems[0].heading = this.getItemLabel(this.selectedTimeZoneItem, open);
  }
  onComboboxBeforeClose(event) {
    event.stopPropagation();
    this.overrideSelectedLabelForRegion(false);
    this.calciteInputTimeZoneBeforeClose.emit();
  }
  onComboboxBeforeOpen(event) {
    event.stopPropagation();
    this.overrideSelectedLabelForRegion(true);
    this.calciteInputTimeZoneBeforeOpen.emit();
  }
  onComboboxChange(event) {
    event.stopPropagation();
    const combobox = event.target;
    const selectedItem = combobox.selectedItems[0];
    const previousValue = this._value;
    if (!selectedItem) {
      this._value = "";
      this.requestUpdate("value", previousValue);
      this.selectedTimeZoneItem = null;
      this.calciteInputTimeZoneChange.emit();
      return;
    }
    const selected = this.findTimeZoneItemByLabel(selectedItem.getAttribute("data-label"));
    const selectedValue = `${selected.value}`;
    if (this.value === selectedValue && selected.label === this.selectedTimeZoneItem.label) {
      return;
    }
    this._value = selectedValue;
    this.requestUpdate("value", previousValue);
    this.selectedTimeZoneItem = selected;
    this.#valueUpdateContext = "user";
    this.calciteInputTimeZoneChange.emit();
  }
  onComboboxClose(event) {
    event.stopPropagation();
    this.open = false;
    this.calciteInputTimeZoneClose.emit();
  }
  onComboboxOpen(event) {
    this.open = true;
    event.stopPropagation();
    this.calciteInputTimeZoneOpen.emit();
  }
  findTimeZoneItem(value) {
    return findTimeZoneItemByProp(this.timeZoneItems, "value", value);
  }
  findTimeZoneItemByLabel(label) {
    return findTimeZoneItemByProp(this.timeZoneItems, "label", label);
  }
  async updateTimeZoneItems() {
    this.timeZoneItems = await this.createTimeZoneItems();
  }
  updateTimeZoneSelection() {
    if (this.value === "" && this.clearable) {
      this.selectedTimeZoneItem = null;
      return;
    }
    const fallbackValue = this.mode === "offset" ? getUserTimeZoneOffset() : getUserTimeZoneName();
    const valueToMatch = this.value === "" || !this.value ? fallbackValue : this.value;
    this.selectedTimeZoneItem = this.findTimeZoneItem(valueToMatch) || this.findTimeZoneItem(fallbackValue);
  }
  async createTimeZoneItems() {
    if (!this.messages._lang || !this.messages) {
      return [];
    }
    return createTimeZoneItems(this.messages._lang, this.messages, this.mode, this.referenceDate instanceof Date ? this.referenceDate : new Date(this.referenceDate ?? Date.now()), this.offsetStyle);
  }
  normalizeValue(value) {
    value = value === void 0 ? "" : value;
    return value ? this.normalizer(value) : value;
  }
  getItemLabel(item, open = this.open) {
    const selected = this.selectedTimeZoneItem === item;
    const { label, metadata } = item;
    return !metadata.country || open || !selected ? label : getSelectedRegionTimeZoneLabel(label, metadata.country, this.messages);
  }
  render() {
    return this.interactiveContainer({ disabled: this.disabled, children: html`<calcite-combobox .clearDisabled=${!this.clearable} .disabled=${this.disabled} .label=${this.messages.chooseTimeZone} .labelText=${this.labelText} lang=${this.messages._lang ?? nothing} .maxItems=${this.maxItems} @calciteComboboxBeforeClose=${this.onComboboxBeforeClose} @calciteComboboxBeforeOpen=${this.onComboboxBeforeOpen} @calciteComboboxChange=${this.onComboboxChange} @calciteComboboxClose=${this.onComboboxClose} @calciteComboboxOpen=${this.onComboboxOpen} .overlayPositioning=${this.overlayPositioning} .placeholder=${this.mode === "name" ? this.messages.namePlaceholder : this.mode === "offset" ? this.messages.offsetPlaceholder : this.messages.regionPlaceholder} placeholder-icon=search .readOnly=${this.readOnly} .required=${this.required} .scale=${this.scale} .selectionMode=${this.clearable ? "single" : "single-persist"} .status=${this.status} .topLayerDisabled=${this.topLayerDisabled} .validationIcon=${this.validationIcon} .validationMessage=${this.validationMessage} ${ref(this.comboboxRef)}>${this.renderItems()}<slot name=${SLOTS.labelContent} slot=${SLOTS$1.labelContent}></slot></calcite-combobox>` });
  }
  renderItems() {
    if (this.mode === "region") {
      return this.renderRegionItems();
    }
    return repeat(this.timeZoneItems, ({ label }) => label, (group) => {
      const selected = this.selectedTimeZoneItem === group;
      const { label, metadata, value } = group;
      return html`<calcite-combobox-item data-label=${label ?? nothing} .heading=${label} .metadata=${metadata} .selected=${selected} .value=${value}></calcite-combobox-item>`;
    });
  }
  renderRegionItems() {
    return this.timeZoneItems.flatMap(({ label, items }) => keyed(label, html`<calcite-combobox-item-group .label=${label}>${repeat(items, ({ label: label2 }) => label2, (item) => {
      const selected = this.selectedTimeZoneItem === item;
      const { label: label2, metadata, value } = item;
      const heading = this.getItemLabel(item);
      return html`<calcite-combobox-item data-label=${label2 ?? nothing} .description=${metadata.country} .heading=${heading} .metadata=${metadata} .selected=${selected} .value=${value}><span class=${safeClassMap(CSS.offset)} slot=content-end>${metadata.offset}</span></calcite-combobox-item>`;
    })}</calcite-combobox-item-group>`));
  }
}
customElement("calcite-input-time-zone", InputTimeZone);
export {
  InputTimeZone
};
