import { consume } from "@lit/context";
import type { ContextType } from "@lit/context";
import { mdiMinus, mdiPlus, mdiThermostat, mdiWaterPercent } from "@mdi/js";
import type { CSSResultGroup, PropertyValues } from "lit";
import { LitElement, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { styleMap } from "lit/directives/style-map";
import type { HASSDomEvent } from "../../common/dom/fire_event";
import { stateActive } from "../../common/entity/state_active";
import { stateColorCss } from "../../common/entity/state_color";
import { clamp } from "../../common/number/clamp";
import { debounce } from "../../common/util/debounce";
import "../../components/ha-big-number";
import "../../components/ha-control-circular-slider";
import "../../components/ha-outlined-icon-button";
import "../../components/ha-svg-icon";
import { UNAVAILABLE } from "../../data/entity/entity";
import { DOMAIN_ATTRIBUTES_UNITS } from "../../data/entity/entity_attributes";
import { apiContext, formattersContext } from "../../data/context";
import type { HumidifierEntity } from "../../data/humidifier";
import {
  HUMIDIFIER_ACTION_MODE,
  HumidifierEntityDeviceClass,
} from "../../data/humidifier";
import {
  createStateControlCircularSliderController,
  stateControlCircularSliderStyle,
} from "../state-control-circular-slider-style";

@customElement("ha-state-control-humidifier-humidity")
export class HaStateControlHumidifierHumidity extends LitElement {
  @property({ attribute: false }) public stateObj!: HumidifierEntity;

  @state()
  @consume({ context: apiContext, subscribe: true })
  private _api!: ContextType<typeof apiContext>;

  @state()
  @consume({ context: formattersContext, subscribe: true })
  private _formatters!: ContextType<typeof formattersContext>;

  @property({ attribute: "show-secondary", type: Boolean })
  public showSecondary = false;

  @property({ attribute: "use-current-as-primary", type: Boolean })
  public showCurrentAsPrimary = false;

  @property({ type: Boolean, attribute: "prevent-interaction-on-scroll" })
  public preventInteractionOnScroll = false;

  @state() private _targetHumidity?: number;

  private _sizeController = createStateControlCircularSliderController(this);

  protected willUpdate(changedProp: PropertyValues<this>): void {
    super.willUpdate(changedProp);
    if (changedProp.has("stateObj")) {
      this._targetHumidity = this.stateObj.attributes.humidity;
    }
  }

  private get _step() {
    return this.stateObj.attributes.target_humidity_step ?? 1;
  }

  private get _min() {
    return this.stateObj.attributes.min_humidity ?? 0;
  }

  private get _max() {
    return this.stateObj.attributes.max_humidity ?? 100;
  }

  private _valueChanged(ev: HASSDomEvent<HASSDomEvents["value-changed"]>) {
    const { value } = ev.detail;
    if (typeof value !== "number" || isNaN(value)) return;
    this._targetHumidity = value;
    this._callService();
  }

  private _valueChanging(ev: HASSDomEvent<HASSDomEvents["value-changing"]>) {
    const { value } = ev.detail;
    if (typeof value !== "number" || isNaN(value)) return;
    this._targetHumidity = value;
  }

  private _debouncedCallService = debounce(() => this._callService(), 1000);

  private _callService() {
    this._api.callService("humidifier", "set_humidity", {
      entity_id: this.stateObj!.entity_id,
      humidity: this._targetHumidity,
    });
  }

  private _handleButton(ev) {
    const step = ev.currentTarget.step as number;

    let humidity = this._targetHumidity ?? this._min;
    humidity += step;
    humidity = clamp(humidity, this._min, this._max);

    this._targetHumidity = humidity;
    this._debouncedCallService();
  }

  private _renderLabel() {
    if (this.stateObj.state === UNAVAILABLE) {
      return html`
        <p class="label disabled">
          ${this._formatters.formatEntityState(this.stateObj, UNAVAILABLE)}
        </p>
      `;
    }

    const action = this.stateObj.attributes.action;

    const isHumidityDisplayed =
      (this.stateObj.attributes.current_humidity != null &&
        this.showCurrentAsPrimary) ||
      (this._targetHumidity != null && !this.showCurrentAsPrimary);

    return html`
      <p class="label">
        ${
          action && action !== "off"
            ? this._formatters.formatEntityAttributeValue(
                this.stateObj,
                "action"
              )
            : isHumidityDisplayed
              ? this._formatters.formatEntityState(this.stateObj)
              : nothing
        }
      </p>
    `;
  }

  private _renderButtons() {
    return html`
      <div class="buttons">
        <ha-outlined-icon-button
          .step=${-this._step}
          @click=${this._handleButton}
        >
          <ha-svg-icon .path=${mdiMinus}></ha-svg-icon>
        </ha-outlined-icon-button>
        <ha-outlined-icon-button
          .step=${this._step}
          @click=${this._handleButton}
        >
          <ha-svg-icon .path=${mdiPlus}></ha-svg-icon>
        </ha-outlined-icon-button>
      </div>
    `;
  }

  private _renderPrimary() {
    const currentHumidity = this.stateObj.attributes.current_humidity;

    if (currentHumidity != null && this.showCurrentAsPrimary) {
      return this._renderCurrent(currentHumidity, "big");
    }

    if (this._targetHumidity != null && !this.showCurrentAsPrimary) {
      return this._renderTarget(this._targetHumidity!, "big");
    }

    if (this.stateObj.state !== UNAVAILABLE) {
      return html`
        <p class="primary-state">
          ${this._formatters.formatEntityState(this.stateObj)}
        </p>
      `;
    }

    return nothing;
  }

  private _renderSecondary() {
    if (!this.showSecondary) {
      return html`<p class="label"></p>`;
    }

    const currentHumidity = this.stateObj.attributes.current_humidity;

    if (currentHumidity != null && !this.showCurrentAsPrimary) {
      return html`
        <p class="label">
          <ha-svg-icon .path=${mdiWaterPercent}></ha-svg-icon>
          ${this._renderCurrent(currentHumidity, "normal")}
        </p>
      `;
    }

    if (this._targetHumidity != null && this.showCurrentAsPrimary) {
      return html`
        <p class="label">
          <ha-svg-icon .path=${mdiThermostat}></ha-svg-icon>
          ${this._renderCurrent(this._targetHumidity, "normal")}
        </p>
      `;
    }

    return html`<p class="label"></p>`;
  }

  private _renderTarget(humidity: number, style: "normal" | "big") {
    const formatOptions: Intl.NumberFormatOptions = {
      maximumFractionDigits: 0,
    };
    if (style === "big") {
      return html`
        <ha-big-number
          .value=${humidity}
          .unit=${DOMAIN_ATTRIBUTES_UNITS.humidifier.current_humidity}
          .formatOptions=${formatOptions}
          unit-position="bottom"
        ></ha-big-number>
      `;
    }

    return html`
      ${this._formatters.formatEntityAttributeValue(
        this.stateObj,
        "humidity",
        humidity
      )}
    `;
  }

  private _renderCurrent(humidity: number, style: "normal" | "big") {
    const formatOptions: Intl.NumberFormatOptions = {
      maximumFractionDigits: 1,
    };
    if (style === "big") {
      return html`
        <ha-big-number
          .value=${humidity}
          .unit=${DOMAIN_ATTRIBUTES_UNITS.humidifier.current_humidity}
          .formatOptions=${formatOptions}
          unit-position="bottom"
        ></ha-big-number>
      `;
    }

    return html`
      ${this._formatters.formatEntityAttributeValue(
        this.stateObj,
        "current_humidity",
        humidity
      )}
    `;
  }

  private _renderInfo() {
    return html`
      <div class="info">
        ${this._renderLabel()}${this._renderPrimary()}${this._renderSecondary()}
      </div>
    `;
  }

  protected render() {
    const stateColor = stateColorCss(this.stateObj);
    const active = stateActive(this.stateObj);

    const action = this.stateObj.attributes.action;

    let actionColor: string | undefined;
    if (action && action !== "idle" && action !== "off" && active) {
      actionColor = stateColorCss(
        this.stateObj,
        HUMIDIFIER_ACTION_MODE[action]
      );
    }

    const targetHumidity = this._targetHumidity;
    const currentHumidity = this.stateObj.attributes.current_humidity;

    const containerSizeClass = this._sizeController.value
      ? ` ${this._sizeController.value}`
      : "";

    if (targetHumidity != null && this.stateObj.state !== UNAVAILABLE) {
      const inverted =
        this.stateObj.attributes.device_class ===
        HumidifierEntityDeviceClass.DEHUMIDIFIER;

      return html`
        <div
          class="container${containerSizeClass}"
          style=${styleMap({
            "--state-color": stateColor,
            "--action-color": actionColor,
          })}
        >
          <ha-control-circular-slider
            .preventInteractionOnScroll=${this.preventInteractionOnScroll}
            .inactive=${!active}
            .mode=${inverted ? "end" : "start"}
            .value=${targetHumidity}
            .min=${this._min}
            .max=${this._max}
            .step=${this._step}
            .current=${currentHumidity}
            @value-changed=${this._valueChanged}
            @value-changing=${this._valueChanging}
          >
          </ha-control-circular-slider>
          ${this._renderInfo()} ${this._renderButtons()}
        </div>
      `;
    }

    return html`
      <div
        class="container${containerSizeClass}"
        style=${styleMap({
          "--state-color": stateColor,
          "--action-color": actionColor,
        })}
      >
        <ha-control-circular-slider
          .preventInteractionOnScroll=${this.preventInteractionOnScroll}
          .current=${currentHumidity}
          .min=${this._min}
          .max=${this._max}
          .step=${this._step}
          disabled
        >
        </ha-control-circular-slider>
        ${this._renderInfo()}
      </div>
    `;
  }

  static get styles(): CSSResultGroup {
    return stateControlCircularSliderStyle;
  }
}

declare global {
  interface HTMLElementTagNameMap {
    "ha-state-control-humidifier-humidity": HaStateControlHumidifierHumidity;
  }
}
