import "@home-assistant/webawesome/dist/components/divider/divider";
import { consume, type ContextType } from "@lit/context";
import { mdiBackspace, mdiCalendarToday } from "@mdi/js";
import "cally";
import type { HassConfig } from "home-assistant-js-websocket/dist/types";
import { css, html, LitElement, nothing } from "lit";
import { customElement, state } from "lit/decorators";
import {
  formatCallyMonthYear,
  formatDateMonthYear,
  formatDateShort,
  formatDateYear,
  formatISODateOnly,
} from "../../common/datetime/format_date";
import { valueToDate } from "../../common/datetime/value_to_date";
import { transform } from "../../common/decorators/transform";
import type {
  HASSDomEvent,
  HASSDomTargetEvent,
} from "../../common/dom/fire_event";
import { configContext, internationalizationContext } from "../../data/context";
import { DialogMixin } from "../../dialogs/dialog-mixin";
import type { HomeAssistantConfig } from "../../types";
import "../ha-button";
import type { DatePickerDialogParams } from "../ha-date-input";
import "../ha-adaptive-popover";
import "../ha-dialog-footer";
import "../ha-icon-button";
import "../ha-icon-button-next";
import "../ha-icon-button-prev";
import { datePickerStyles } from "./styles";

type CalendarDate = HTMLElementTagNameMap["calendar-date"];

/**
 * A date picker dialog component that displays a calendar for selecting dates.
 * Uses the `cally` library for calendar rendering and supports localization,
 * min/max date constraints, and optional clearing of the selected date.
 *
 * @element ha-dialog-date-picker
 * Uses {@link DialogMixin} with {@link DatePickerDialogParams} to manage dialog state and parameters.
 */
@customElement("ha-dialog-date-picker")
export class HaDialogDatePicker extends DialogMixin<DatePickerDialogParams>(
  LitElement
) {
  @state()
  @consume({ context: internationalizationContext, subscribe: true })
  private _i18n!: ContextType<typeof internationalizationContext>;

  @state()
  @consume({ context: configContext, subscribe: true })
  @transform<HomeAssistantConfig, HassConfig>({
    transformer: ({ config }) => config,
  })
  private _hassConfig!: HassConfig;

  @state() private _value?: {
    year: string;
    title: string;
    dateString: string;
  };

  /** used to show month and year in calendar-date header */
  @state() private _pickerMonthYear?: string;

  /**
   * used for today to navigate focus in cally-calendar-date
   *
   * Always mirrors the day calendar-date has focused, never cleared: once this
   * has held a date, rendering `undefined` over it makes cally fall back to the
   * selected value and page the calendar away from where the user is.
   */
  @state() private _focusDate?: string;

  public connectedCallback() {
    super.connectedCallback();

    if (this.params) {
      const date = valueToDate(this.params.value);

      this._pickerMonthYear = formatDateMonthYear(
        date,
        this._i18n.locale,
        this._hassConfig
      );

      this._value = this.params.value
        ? {
            year: formatDateYear(date, this._i18n.locale, this._hassConfig),
            title: formatDateShort(date, this._i18n.locale, this._hassConfig),
            dateString: formatISODateOnly(
              date,
              this._i18n.locale,
              this._hassConfig
            ),
          }
        : undefined;
    }
  }

  render() {
    if (!this.params) {
      return nothing;
    }

    return html`<ha-adaptive-popover
      .dialogAnchor=${this.dialogAnchor}
      open
      width="small"
      .headerTitle=${
        this._value?.title ||
        this._i18n.localize("ui.dialogs.date-picker.title")
      }
      .headerSubtitle=${this._value?.year}
      header-subtitle-position="above"
    >
      ${
        this.params.canClear
          ? html`
              <ha-icon-button
                .path=${mdiBackspace}
                .label=${this._i18n.localize("ui.dialogs.date-picker.clear")}
                slot="headerActionItems"
                @click=${this._clear}
              ></ha-icon-button>
            `
          : nothing
      }
      <wa-divider></wa-divider>
      <calendar-date
        .value=${this._value?.dateString}
        .min=${this.params.min}
        .max=${this.params.max}
        .locale=${this.params.locale}
        .firstDayOfWeek=${this.params.firstWeekday}
        .focusedDate=${this._focusDate}
        @change=${this._valueChanged}
        @focusday=${this._focusChanged}
      >
        <ha-icon-button-prev
          tabindex="-1"
          slot="previous"
        ></ha-icon-button-prev>
        <div class="heading" slot="heading">
          <span class="month-year">${this._pickerMonthYear}</span>
          <ha-icon-button
            @click=${this._setToday}
            .path=${mdiCalendarToday}
            .label=${this._i18n.localize("ui.dialogs.date-picker.today")}
          ></ha-icon-button>
        </div>
        <ha-icon-button-next tabindex="-1" slot="next"></ha-icon-button-next>
        <calendar-month></calendar-month>
      </calendar-date>
      <ha-dialog-footer slot="footer">
        <ha-button
          appearance="plain"
          slot="secondaryAction"
          @click=${this.closeDialog}
        >
          ${this._i18n.localize("ui.common.cancel")}
        </ha-button>
        <ha-button slot="primaryAction" @click=${this._setValue}>
          ${this._i18n.localize("ui.common.ok")}
        </ha-button>
      </ha-dialog-footer>
    </ha-adaptive-popover>`;
  }

  private _valueChanged(ev: HASSDomTargetEvent<CalendarDate>) {
    const dateElement = ev.target as CalendarDate;
    if (dateElement.value) {
      this._updateValue(dateElement.value);
    }
  }

  private _updateValue(value?: string, setFocusDay = false) {
    const date = valueToDate(value);
    this._value = {
      year: formatDateYear(date, this._i18n.locale, this._hassConfig),
      title: formatDateShort(date, this._i18n.locale, this._hassConfig),
      dateString:
        value || formatISODateOnly(date, this._i18n.locale, this._hassConfig),
    };

    if (setFocusDay) {
      this._focusDate = this._value.dateString;
      this._pickerMonthYear = formatDateMonthYear(
        date,
        this._i18n.locale,
        this._hassConfig
      );
    }
  }

  private _focusChanged(
    ev: HASSDomEvent<Date> & HASSDomTargetEvent<CalendarDate>
  ) {
    this._pickerMonthYear = formatCallyMonthYear(ev.detail, this._i18n.locale);
    this._focusDate = ev.target.focusedDate;
  }

  private _clear() {
    this.params?.onChange(undefined);
    this.closeDialog();
  }

  private _setToday() {
    this._updateValue(undefined, true);
  }

  private _setValue() {
    if (!this._value) {
      // Date picker opens to today if value is undefined. If user click OK
      // without changing the date, should return todays date, not undefined.
      this._setToday();
    }
    this.params?.onChange(this._value?.dateString);
    this.closeDialog();
  }

  static styles = [
    datePickerStyles,
    css`
      ha-adaptive-popover {
        --dialog-content-padding: 0;
      }
    `,
  ];
}

declare global {
  interface HTMLElementTagNameMap {
    "ha-dialog-date-picker": HaDialogDatePicker;
  }
}
