import { mdiPlayBox, mdiPlus } from "@mdi/js";
import type { PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { fireEvent } from "../../common/dom/fire_event";
import { supportsFeature } from "../../common/entity/supports-feature";
import type { MediaPickedEvent } from "../../data/media-player";
import {
  MediaClassBrowserSettings,
  MediaPlayerEntityFeature,
} from "../../data/media-player";
import type { MediaSelector, MediaSelectorValue } from "../../data/selector";
import type { HomeAssistant } from "../../types";
import "../ha-alert";
import "../ha-form/ha-form";
import type { SchemaUnion } from "../ha-form/types";
import "../media-player/ha-media-browser-thumbnail";
import { showMediaBrowserDialog } from "../media-player/show-media-browser-dialog";
import { ensureArray } from "../../common/array/ensure-array";
import "../ha-picture-upload";

const MANUAL_SCHEMA = [
  { name: "media_content_id", required: false, selector: { text: {} } },
  { name: "media_content_type", required: false, selector: { text: {} } },
] as const;

const INCLUDE_DOMAINS = ["media_player"];

const EMPTY_FORM = {};

@customElement("ha-selector-media")
export class HaMediaSelector extends LitElement {
  @property({ attribute: false }) public hass!: HomeAssistant;

  @property({ attribute: false }) public selector!: MediaSelector;

  @property({ attribute: false }) public value?: MediaSelectorValue;

  @property() public label?: string;

  @property() public helper?: string;

  @property({ type: Boolean, reflect: true }) public disabled = false;

  @property({ type: Boolean, reflect: true }) public required = true;

  @property({ attribute: false }) public context?: {
    filter_entity?: string | string[];
  };

  private _contextEntities: string[] | undefined;

  private get _hasAccept(): boolean {
    return !!this.selector?.media?.accept?.length;
  }

  willUpdate(changedProps: PropertyValues<this>) {
    if (changedProps.has("context")) {
      if (!this._hasAccept) {
        this._contextEntities = ensureArray(this.context?.filter_entity);
      }
    }
  }

  protected render() {
    const entityId = this._getActiveEntityId();

    const stateObj = entityId ? this.hass.states[entityId] : undefined;

    const supportsBrowse =
      !entityId ||
      (stateObj &&
        supportsFeature(stateObj, MediaPlayerEntityFeature.BROWSE_MEDIA));

    if (this.selector.media?.image_upload && !this.value) {
      return html`${this.label ? html`<label>${this.label}</label>` : nothing}
        <ha-picture-upload
          .hass=${this.hass}
          .value=${null}
          .contentIdHelper=${this.selector.media?.content_id_helper}
          select-media
          full-media
          @media-picked=${this._pictureUploadMediaPicked}
        ></ha-picture-upload>`;
    }

    return html`
      ${
        this._hasAccept ||
        (this._contextEntities && this._contextEntities.length <= 1)
          ? nothing
          : html`
              <ha-entity-picker
                .value=${entityId}
                .label=${
                  this.label ||
                  this.hass.localize(
                    "ui.components.selectors.media.pick_media_player"
                  )
                }
                .disabled=${this.disabled}
                .helper=${this.helper}
                .required=${this.required}
                .hideClearIcon=${!!this._contextEntities}
                .includeDomains=${INCLUDE_DOMAINS}
                .includeEntities=${this._contextEntities}
                .allowCustomEntity=${!this._contextEntities}
                @value-changed=${this._entityChanged}
              ></ha-entity-picker>
            `
      }
      ${
        !supportsBrowse
          ? html`
              ${this.label ? html`<label>${this.label}</label>` : nothing}
              <ha-alert>
                ${this.hass.localize(
                  "ui.components.selectors.media.browse_not_supported"
                )}
              </ha-alert>
              <ha-form
                .hass=${this.hass}
                .data=${this.value || EMPTY_FORM}
                .schema=${MANUAL_SCHEMA}
                .computeLabel=${this._computeLabelCallback}
                .computeHelper=${this._computeHelperCallback}
              ></ha-form>
            `
          : html`${this.label ? html`<label>${this.label}</label>` : nothing}
              <ha-card
                outlined
                tabindex="0"
                role="button"
                aria-label=${
                  !this.value?.media_content_id
                    ? this.hass.localize(
                        "ui.components.selectors.media.pick_media"
                      )
                    : this.value.metadata?.title || this.value.media_content_id
                }
                @click=${this._pickMedia}
                @keydown=${this._handleKeyDown}
                class=${
                  this.disabled || (!entityId && !this._hasAccept)
                    ? "disabled"
                    : ""
                }
              >
                <div class="content-container">
                  <div class="thumbnail">
                    ${
                      this.value?.metadata?.thumbnail
                        ? html`
                            <div
                              class="${classMap({
                                "centered-image":
                                  !!this.value.metadata.media_class &&
                                  ["app", "directory"].includes(
                                    this.value.metadata.media_class
                                  ),
                              })}
                          image"
                            >
                              <ha-media-browser-thumbnail
                                .hass=${this.hass}
                                .url=${this.value.metadata.thumbnail}
                              ></ha-media-browser-thumbnail>
                            </div>
                          `
                        : html`
                            <div class="icon-holder image">
                              <ha-svg-icon
                                class="folder"
                                .path=${
                                  !this.value?.media_content_id
                                    ? mdiPlus
                                    : this.value?.metadata?.media_class
                                      ? MediaClassBrowserSettings[
                                          this.value.metadata.media_class ===
                                          "directory"
                                            ? this.value.metadata
                                                .children_media_class ||
                                              this.value.metadata.media_class
                                            : this.value.metadata.media_class
                                        ].icon
                                      : mdiPlayBox
                                }
                              ></ha-svg-icon>
                            </div>
                          `
                    }
                  </div>
                  <div class="title">
                    ${
                      !this.value?.media_content_id
                        ? this.hass.localize(
                            "ui.components.selectors.media.pick_media"
                          )
                        : this.value.metadata?.title ||
                          this.value.media_content_id
                    }
                  </div>
                </div>
              </ha-card>
              ${
                this.selector.media?.clearable
                  ? html`<div>
                      <ha-button
                        appearance="plain"
                        size="s"
                        variant="danger"
                        @click=${this._clearValue}
                      >
                        ${this.hass.localize(
                          "ui.components.picture-upload.clear_picture"
                        )}
                      </ha-button>
                    </div>`
                  : nothing
              }`
      }
    `;
  }

  private _computeLabelCallback = (
    schema: SchemaUnion<typeof MANUAL_SCHEMA>
  ): string =>
    this.hass.localize(`ui.components.selectors.media.${schema.name}`);

  private _computeHelperCallback = (
    schema: SchemaUnion<typeof MANUAL_SCHEMA>
  ): string =>
    this.hass.localize(`ui.components.selectors.media.${schema.name}_detail`);

  private _entityChanged(ev: CustomEvent) {
    ev.stopPropagation();
    if (!this._hasAccept && this.context?.filter_entity) {
      fireEvent(this, "value-changed", {
        value: {
          media_content_id: "",
          media_content_type: "",
          metadata: {
            browse_entity_id: ev.detail.value,
          },
        },
      });
    } else {
      fireEvent(this, "value-changed", {
        value: {
          entity_id: ev.detail.value,
          media_content_id: "",
          media_content_type: "",
        },
      });
    }
  }

  private _pickMedia() {
    showMediaBrowserDialog(this, {
      action: "pick",
      entityId: this._getActiveEntityId(),
      navigateIds: this.value?.metadata?.navigateIds,
      accept: this.selector.media?.accept,
      defaultId: this.value?.media_content_id,
      defaultType: this.value?.media_content_type,
      hideContentType: this.selector.media?.hide_content_type,
      contentIdHelper: this.selector.media?.content_id_helper,
      mediaPickedCallback: (pickedMedia: MediaPickedEvent) => {
        fireEvent(this, "value-changed", {
          value: {
            ...this.value,
            media_content_id: pickedMedia.item.media_content_id,
            media_content_type: pickedMedia.item.media_content_type,
            metadata: {
              title: pickedMedia.item.title,
              thumbnail: pickedMedia.item.thumbnail,
              media_class: pickedMedia.item.media_class,
              children_media_class: pickedMedia.item.children_media_class,
              navigateIds: pickedMedia.navigateIds?.map((id) => ({
                media_content_type: id.media_content_type,
                media_content_id: id.media_content_id,
              })),
              ...(!this._hasAccept && this.context?.filter_entity
                ? { browse_entity_id: this._getActiveEntityId() }
                : {}),
            },
          },
        });
      },
    });
  }

  private _getActiveEntityId(): string | undefined {
    const metaId = this.value?.metadata?.browse_entity_id;
    return (
      this.value?.entity_id ||
      (metaId && this._contextEntities?.includes(metaId) && metaId) ||
      this._contextEntities?.[0]
    );
  }

  private _handleKeyDown(ev: KeyboardEvent) {
    if (ev.key === "Enter" || ev.key === " ") {
      ev.preventDefault();
      this._pickMedia();
    }
  }

  private _pictureUploadMediaPicked(ev) {
    const pickedMedia = ev.detail as MediaPickedEvent;
    fireEvent(this, "value-changed", {
      value: {
        ...this.value,
        media_content_id: pickedMedia.item.media_content_id,
        media_content_type: pickedMedia.item.media_content_type,
        metadata: {
          title: pickedMedia.item.title,
          thumbnail: pickedMedia.item.thumbnail,
          media_class: pickedMedia.item.media_class,
          children_media_class: pickedMedia.item.children_media_class,
          navigateIds: pickedMedia.navigateIds?.map((id) => ({
            media_content_type: id.media_content_type,
            media_content_id: id.media_content_id,
          })),
        },
      },
    });
  }

  private _clearValue() {
    fireEvent(this, "value-changed", { value: undefined });
  }

  static styles = css`
    ha-entity-picker {
      display: block;
      margin-bottom: 16px;
    }
    ha-alert {
      display: block;
      margin-bottom: 16px;
    }
    ha-card {
      position: relative;
      width: 100%;
      box-sizing: border-box;
      cursor: pointer;
      transition: background-color 180ms ease-in-out;
      min-height: 56px;
    }
    ha-card:hover:not(.disabled),
    ha-card:focus:not(.disabled) {
      background-color: var(--state-icon-hover-color, rgba(0, 0, 0, 0.04));
    }
    ha-card:focus {
      outline: none;
    }
    ha-card.disabled {
      pointer-events: none;
      color: var(--disabled-text-color);
    }
    .content-container {
      display: flex;
      align-items: center;
      padding: 8px;
      gap: var(--ha-space-3);
    }
    ha-card .thumbnail {
      width: 40px;
      height: 40px;
      flex-shrink: 0;
      position: relative;
      box-sizing: border-box;
      border-radius: var(--ha-border-radius-md);
      overflow: hidden;
    }
    ha-card .image {
      border-radius: var(--ha-border-radius-md);
    }
    .folder {
      --mdc-icon-size: 24px;
    }
    .title {
      font-size: var(--ha-font-size-m);
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
      line-height: 1.4;
      flex: 1;
      min-width: 0;
    }
    .image {
      position: absolute;
      top: 0;
      right: 0;
      left: 0;
      bottom: 0;
      --ha-media-browser-thumbnail-fit: cover;
    }
    .centered-image {
      margin: 4px;
      --ha-media-browser-thumbnail-fit: contain;
    }
    .icon-holder {
      display: flex;
      justify-content: center;
      align-items: center;
      width: 100%;
      height: 100%;
    }
  `;
}

declare global {
  interface HTMLElementTagNameMap {
    "ha-selector-media": HaMediaSelector;
  }
}
