import { consume } from "@lit/context";
import { mdiDelete, mdiFileUpload } from "@mdi/js";
import type { PropertyValues, TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { ensureArray } from "../common/array/ensure-array";
import { consumeLocalize } from "../common/decorators/consume-context-entry";
import { transform } from "../common/decorators/transform";
import { fireEvent } from "../common/dom/fire_event";
import { blankBeforePercent } from "../common/translations/blank_before_percent";
import type { LocalizeFunc } from "../common/translations/localize";
import { internationalizationContext } from "../data/context";
import type { FrontendLocaleData } from "../data/translation";
import type { HomeAssistantInternationalization } from "../types";
import { bytesToString } from "../util/bytes-to-string";
import "./ha-button";
import "./ha-icon-button";
import "./progress/ha-progress-bar";

declare global {
  interface HASSDomEvents {
    "file-picked": { files: File[] };
    "files-cleared": undefined;
  }
}

@customElement("ha-file-upload")
export class HaFileUpload extends LitElement {
  @property({ attribute: false }) public localize?: LocalizeFunc;

  @state() @consumeLocalize() private _localize?: LocalizeFunc;

  @state()
  @consume({ context: internationalizationContext, subscribe: true })
  @transform<HomeAssistantInternationalization, FrontendLocaleData>({
    transformer: ({ locale }) => locale,
  })
  private _locale?: FrontendLocaleData;

  @property() public accept!: string;

  @property() public icon?: string;

  @property() public label?: string;

  @property() public secondary?: string;

  @property({ attribute: "uploading-label" }) public uploadingLabel?: string;

  @property({ attribute: "delete-label" }) public deleteLabel?: string;

  @property() public supports?: string;

  @property({ type: Object }) public value?: File | File[] | FileList | string;

  @property({ type: Boolean }) public multiple = false;

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

  @property({ type: Boolean }) public uploading = false;

  @property({ type: Number }) public progress?: number;

  @property({ type: Boolean, attribute: "auto-open-file-dialog" })
  public autoOpenFileDialog = false;

  @state() private _drag = false;

  @query("#input") private _input?: HTMLInputElement;

  protected firstUpdated(changedProperties: PropertyValues<this>) {
    super.firstUpdated(changedProperties);
    if (this.autoOpenFileDialog) {
      this._openFilePicker();
    }
  }

  private get _name() {
    if (this.value === undefined) {
      return "";
    }
    if (typeof this.value === "string") {
      return this.value;
    }
    const files =
      this.value instanceof FileList
        ? Array.from(this.value)
        : ensureArray(this.value);

    return files.map((file) => file.name).join(", ");
  }

  public render(): TemplateResult {
    const localize = this.localize || this._localize!;
    return html`
      ${this.uploading
        ? html`<div class="container">
            <div class="uploading">
              <span class="header"
                >${this.uploadingLabel ||
                (this.value
                  ? localize("ui.components.file-upload.uploading_name", {
                      name: this._name,
                    })
                  : localize("ui.components.file-upload.uploading"))}</span
              >
              ${this.progress
                ? html`<div class="progress">
                    ${this.progress}${this._locale &&
                    blankBeforePercent(this._locale)}%
                  </div>`
                : nothing}
            </div>
            <ha-progress-bar
              .indeterminate=${!this.progress}
              .value=${this.progress}
              loading
            ></ha-progress-bar>
          </div>`
        : html`<label
            for=${this.value ? "" : "input"}
            class="container ${classMap({
              dragged: this._drag,
              multiple: this.multiple,
              value: Boolean(this.value),
            })}"
            @drop=${this._handleDrop}
            @dragenter=${this._handleDragStart}
            @dragover=${this._handleDragStart}
            @dragleave=${this._handleDragEnd}
            @dragend=${this._handleDragEnd}
            >${!this.value
              ? html`<ha-button
                    size="s"
                    appearance="filled"
                    @click=${this._openFilePicker}
                  >
                    <ha-svg-icon
                      slot="start"
                      .path=${this.icon || mdiFileUpload}
                    ></ha-svg-icon>
                    ${this.label || localize("ui.components.file-upload.label")}
                  </ha-button>
                  <span class="secondary"
                    >${this.secondary ||
                    localize("ui.components.file-upload.secondary")}</span
                  >
                  <span class="supports">${this.supports}</span>`
              : typeof this.value === "string"
                ? html`<div class="row">
                    <div class="value" @click=${this._openFilePicker}>
                      <ha-svg-icon
                        .path=${this.icon || mdiFileUpload}
                      ></ha-svg-icon>
                      ${this.value}
                    </div>
                    <ha-icon-button
                      @click=${this._clearValue}
                      .label=${this.deleteLabel || localize("ui.common.delete")}
                      .path=${mdiDelete}
                    ></ha-icon-button>
                  </div>`
                : (this.value instanceof FileList
                    ? Array.from(this.value)
                    : ensureArray(this.value)
                  ).map(
                    (file) =>
                      html`<div class="row">
                        <div class="value" @click=${this._openFilePicker}>
                          <ha-svg-icon
                            .path=${this.icon || mdiFileUpload}
                          ></ha-svg-icon>
                          ${file.name} - ${bytesToString(file.size)}
                        </div>
                        <ha-icon-button
                          @click=${this._clearValue}
                          .label=${this.deleteLabel ||
                          localize("ui.common.delete")}
                          .path=${mdiDelete}
                        ></ha-icon-button>
                      </div>`
                  )}
            <input
              id="input"
              type="file"
              class="file"
              .accept=${this.accept}
              .multiple=${this.multiple}
              @change=${this._handleFilePicked}
          /></label>`}
    `;
  }

  private _openFilePicker() {
    this._input?.click();
  }

  private _handleDrop(ev: DragEvent) {
    ev.preventDefault();
    ev.stopPropagation();
    if (ev.dataTransfer?.files) {
      fireEvent(this, "file-picked", {
        files:
          this.multiple || ev.dataTransfer.files.length === 1
            ? Array.from(ev.dataTransfer.files)
            : [ev.dataTransfer.files[0]],
      });
    }
    this._drag = false;
  }

  private _handleDragStart(ev: DragEvent) {
    ev.preventDefault();
    ev.stopPropagation();
    this._drag = true;
  }

  private _handleDragEnd(ev: DragEvent) {
    ev.preventDefault();
    ev.stopPropagation();
    this._drag = false;
  }

  private _handleFilePicked(ev) {
    if (ev.target.files.length === 0) {
      return;
    }
    this.value = ev.target.files;
    fireEvent(this, "file-picked", { files: ev.target.files });
  }

  private _clearValue(ev: Event) {
    ev.preventDefault();
    this._input!.value = "";
    this.value = undefined;
    fireEvent(this, "change");
    fireEvent(this, "files-cleared");
  }

  static styles = css`
    :host {
      display: block;
      height: 240px;
    }
    :host([disabled]) {
      pointer-events: none;
      color: var(--disabled-text-color);
    }
    .container {
      position: relative;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      border: solid 1px
        var(--mdc-text-field-idle-line-color, rgba(0, 0, 0, 0.42));
      border-radius: var(--mdc-shape-small, var(--ha-border-radius-sm));
      height: 100%;
    }
    .row {
      display: flex;
      align-items: center;
    }
    label.container {
      border: dashed 1px
        var(--mdc-text-field-idle-line-color, rgba(0, 0, 0, 0.42));
      cursor: pointer;
    }
    .container .uploading {
      display: flex;
      flex-direction: column;
      width: 100%;
      align-items: flex-start;
      padding: 0 32px;
      box-sizing: border-box;
    }
    :host([disabled]) .container {
      border-color: var(--disabled-color);
    }
    label:hover,
    label.dragged {
      border-style: solid;
    }
    label.dragged {
      border-color: var(--primary-color);
    }
    .dragged:before {
      position: absolute;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      background-color: var(--primary-color);
      content: "";
      opacity: var(--dark-divider-opacity);
      pointer-events: none;
      border-radius: var(--mdc-shape-small, 4px);
    }
    label.value {
      cursor: default;
    }
    label.value.multiple {
      justify-content: unset;
      overflow: auto;
    }
    .highlight {
      color: var(--primary-color);
    }
    ha-button {
      margin-bottom: 8px;
    }
    .supports {
      color: var(--secondary-text-color);
      font-size: var(--ha-font-size-s);
    }
    :host([disabled]) .secondary {
      color: var(--disabled-text-color);
    }
    input.file {
      display: none;
    }
    .value {
      cursor: pointer;
    }
    .value ha-svg-icon {
      margin-right: 8px;
      margin-inline-end: 8px;
      margin-inline-start: initial;
    }
    ha-button {
      --mdc-button-outline-color: var(--primary-color);
      --ha-icon-button-size: 24px;
    }
    ha-progress-bar {
      width: 100%;
      padding: 8px 32px;
      box-sizing: border-box;
    }
    .header {
      font-weight: var(--ha-font-weight-medium);
    }
    .progress {
      color: var(--secondary-text-color);
    }
    button.link {
      background: none;
      border: none;
      padding: 0;
      font-size: var(--ha-font-size-m);
      color: var(--primary-color);
      text-decoration: underline;
      cursor: pointer;
    }
  `;
}

declare global {
  interface HTMLElementTagNameMap {
    "ha-file-upload": HaFileUpload;
  }
}
