import type { PropertyValues, TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { ifDefined } from "lit/directives/if-defined";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { computeDomain } from "../../../common/entity/compute_domain";
import { computeStateName } from "../../../common/entity/compute_state_name";
import "../../../components/entity/state-badge";
import "../../../components/ha-card";
import "../../../components/ha-icon";
import "../../../components/ha-relative-time";
import { isUnavailableState } from "../../../data/entity";
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
import type {
  CallServiceActionConfig,
  MoreInfoActionConfig,
} from "../../../data/lovelace/config/action";
import { SENSOR_DEVICE_CLASS_TIMESTAMP } from "../../../data/sensor";
import type { HomeAssistant } from "../../../types";
import { actionHandler } from "../common/directives/action-handler-directive";
import { findEntities } from "../common/find-entities";
import { handleAction } from "../common/handle-action";
import { hasAction, hasAnyAction } from "../common/has-action";
import { hasConfigOrEntitiesChanged } from "../common/has-changed";
import { processConfigEntities } from "../common/process-config-entities";
import "../components/hui-timestamp-display";
import { createEntityNotFoundWarning } from "../components/hui-warning";
import "../components/hui-warning-element";
import type { LovelaceCard, LovelaceCardEditor } from "../types";
import type { GlanceCardConfig, GlanceConfigEntity } from "./types";

@customElement("hui-glance-card")
export class HuiGlanceCard extends LitElement implements LovelaceCard {
  public static async getConfigElement(): Promise<LovelaceCardEditor> {
    await import("../editor/config-elements/hui-glance-card-editor");
    return document.createElement("hui-glance-card-editor");
  }

  public static getStubConfig(
    hass: HomeAssistant,
    entities: string[],
    entitiesFallback: string[]
  ): GlanceCardConfig {
    const includeDomains = ["sensor"];
    const maxEntities = 3;
    const foundEntities = findEntities(
      hass,
      maxEntities,
      entities,
      entitiesFallback,
      includeDomains
    );

    return { type: "glance", entities: foundEntities };
  }

  @property({ attribute: false }) public hass?: HomeAssistant;

  @state() private _config?: GlanceCardConfig;

  private _configEntities?: GlanceConfigEntity[];

  public getCardSize(): number {
    const rowHeight =
      (this._config!.show_icon ? 1 : 0) +
      (this._config!.show_name ? 1 : 0) +
      (this._config!.show_state ? 1 : 0);

    const numRows = Math.ceil(
      this._configEntities!.length / (this._config!.columns || 5)
    );

    return (this._config!.title ? 2 : 0) + rowHeight * numRows;
  }

  public setConfig(config: GlanceCardConfig): void {
    this._config = {
      show_name: true,
      show_state: true,
      show_icon: true,
      state_color: true,
      ...config,
    };
    const entities = processConfigEntities(config.entities).map(
      (entityConf) => ({
        hold_action: { action: "more-info" } as MoreInfoActionConfig,
        ...entityConf,
      })
    );

    for (const entity of entities) {
      if (
        (entity.tap_action &&
          entity.tap_action.action === "call-service" &&
          !entity.tap_action.service) ||
        (entity.hold_action &&
          entity.hold_action.action === "call-service" &&
          !(entity.hold_action as CallServiceActionConfig).service)
      ) {
        throw new Error(
          'Missing required property "service" when tap_action or hold_action is call-service'
        );
      }
    }

    const columns = config.columns || Math.min(config.entities.length, 5);
    this.style.setProperty("--glance-column-width", `${100 / columns}%`);

    this._configEntities = entities;

    if (this.hass) {
      this.requestUpdate();
    }
  }

  protected shouldUpdate(changedProps: PropertyValues): boolean {
    return hasConfigOrEntitiesChanged(this, changedProps);
  }

  protected render() {
    if (!this._config || !this.hass) {
      return nothing;
    }
    const { title } = this._config;

    return html`
      <ha-card .header=${title}>
        <div class=${classMap({ entities: true, "no-header": !title })}>
          ${this._configEntities!.map((entityConf) =>
            this._renderEntity(entityConf)
          )}
        </div>
      </ha-card>
    `;
  }

  protected updated(changedProps: PropertyValues): void {
    super.updated(changedProps);
    if (!this._config || !this.hass) {
      return;
    }

    const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
    const oldConfig = changedProps.get("_config") as
      | GlanceCardConfig
      | undefined;

    if (
      !oldHass ||
      !oldConfig ||
      oldHass.themes !== this.hass.themes ||
      oldConfig.theme !== this._config.theme
    ) {
      applyThemesOnElement(this, this.hass.themes, this._config.theme);
    }
  }

  static styles = css`
    ha-card {
      height: 100%;
    }
    .entities {
      display: flex;
      padding: 0 16px 4px;
      flex-wrap: wrap;
      box-sizing: border-box;
      align-items: center;
      align-content: center;
    }
    .entities.no-header {
      padding-top: 16px;
    }
    .entity {
      box-sizing: border-box;
      padding: 0 4px;
      display: flex;
      flex-direction: column;
      align-items: center;
      margin-bottom: 12px;
      width: var(--glance-column-width, 20%);
    }
    .entity.action {
      cursor: pointer;
    }
    .entity:focus {
      outline: none;
      background: var(--divider-color);
      border-radius: 14px;
      padding: 4px;
      margin-top: -4px;
      margin-bottom: 8px;
    }
    .entity div {
      width: 100%;
      text-align: center;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .name {
      min-height: var(--ha-line-height-normal);
    }
    .warning {
      cursor: default;
      position: relative;
      padding: 8px;
      width: calc(var(--glance-column-width, 20%) - 4px);
      margin: 0 2px;
    }
    .warning::before {
      position: absolute;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      opacity: 0.12;
      pointer-events: none;
      content: "";
      border-radius: 4px;
      background-color: var(--warning-color);
    }
    state-badge {
      margin: 8px 0;
    }
    hui-warning-element {
      padding: 8px;
    }
  `;

  private _renderEntity(entityConf: GlanceConfigEntity): TemplateResult {
    const stateObj = this.hass!.states[entityConf.entity];

    if (!stateObj) {
      return html`<div class="entity warning">
        ${this._config!.show_name
          ? html`
              <div class="name">
                ${createEntityNotFoundWarning(this.hass!, entityConf.entity)}
              </div>
            `
          : ""}
        ${this._config!.show_icon
          ? html` <hui-warning-element
              .label=${createEntityNotFoundWarning(
                this.hass!,
                entityConf.entity
              )}
            ></hui-warning-element>`
          : ""}
        <div>${this._config!.show_state ? entityConf.entity : ""}</div>
      </div>`;
    }

    const name = entityConf.name ?? computeStateName(stateObj);

    return html`
      <div
        class=${classMap({ entity: true, action: hasAnyAction(entityConf) })}
        .config=${entityConf}
        @action=${this._handleAction}
        .actionHandler=${actionHandler({
          hasHold: hasAction(entityConf.hold_action),
          hasDoubleClick: hasAction(entityConf.double_tap_action),
        })}
        tabindex=${ifDefined(
          !entityConf.tap_action || hasAction(entityConf.tap_action)
            ? "0"
            : undefined
        )}
      >
        ${this._config!.show_name
          ? html` <div class="name" .title=${name}>${name}</div> `
          : ""}
        ${this._config!.show_icon
          ? html`
              <state-badge
                .hass=${this.hass}
                .stateObj=${stateObj}
                .overrideIcon=${entityConf.icon}
                .overrideImage=${entityConf.image}
                .stateColor=${entityConf.state_color ??
                this._config!.state_color}
              ></state-badge>
            `
          : ""}
        ${this._config!.show_state && entityConf.show_state !== false
          ? html`
              <div>
                ${computeDomain(entityConf.entity) === "sensor" &&
                stateObj.attributes.device_class ===
                  SENSOR_DEVICE_CLASS_TIMESTAMP &&
                !isUnavailableState(stateObj.state)
                  ? html`
                      <hui-timestamp-display
                        .hass=${this.hass}
                        .ts=${new Date(stateObj.state)}
                        .format=${entityConf.format}
                        capitalize
                      ></hui-timestamp-display>
                    `
                  : entityConf.show_last_changed
                    ? html`
                        <ha-relative-time
                          .hass=${this.hass}
                          .datetime=${stateObj.last_changed}
                          capitalize
                        ></ha-relative-time>
                      `
                    : this.hass!.formatEntityState(stateObj)}
              </div>
            `
          : ""}
      </div>
    `;
  }

  private _handleAction(ev: ActionHandlerEvent) {
    const config = (ev.currentTarget as any).config as GlanceConfigEntity;
    handleAction(this, this.hass!, config, ev.detail.action!);
  }
}

declare global {
  interface HTMLElementTagNameMap {
    "hui-glance-card": HuiGlanceCard;
  }
}
