import {
  mdiCodeBraces,
  mdiContentCopy,
  mdiContentCut,
  mdiDelete,
  mdiListBoxOutline,
  mdiPlus,
} from "@mdi/js";
import deepClone from "deep-clone-simple";
import type { CSSResultGroup } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { keyed } from "lit/directives/keyed";
import {
  any,
  array,
  assert,
  assign,
  object,
  optional,
  string,
} from "superstruct";
import { storage } from "../../../../common/decorators/storage";
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
import { fireEvent } from "../../../../common/dom/fire_event";
import type {
  HaFormSchema,
  SchemaUnion,
} from "../../../../components/ha-form/types";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-icon-button-arrow-next";
import "../../../../components/ha-icon-button-arrow-prev";
import "../../../../components/ha-tab-group";
import "../../../../components/ha-tab-group-tab";
import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
import type { LovelaceConfig } from "../../../../data/lovelace/config/types";
import type { HomeAssistant } from "../../../../types";
import type { StackCardConfig } from "../../cards/types";
import type { LovelaceCardEditor } from "../../types";
import "../card-editor/hui-card-element-editor";
import type { HuiCardElementEditor } from "../card-editor/hui-card-element-editor";
import "../card-editor/hui-card-picker";
import type { ConfigChangedEvent } from "../hui-element-editor";
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
import type { GUIModeChangedEvent } from "../types";
import { configElementStyle } from "./config-elements-style";

const cardConfigStruct = assign(
  baseLovelaceCardConfig,
  object({
    cards: array(any()),
    title: optional(string()),
  })
);

const SCHEMA = [
  {
    name: "title",
    selector: { text: {} },
  },
] as const;

@customElement("hui-stack-card-editor")
export class HuiStackCardEditor
  extends LitElement
  implements LovelaceCardEditor
{
  @property({ attribute: false }) public hass?: HomeAssistant;

  @property({ attribute: false }) public lovelace?: LovelaceConfig;

  @storage({
    key: "dashboardCardClipboard",
    state: false,
    subscribe: false,
    storage: "sessionStorage",
  })
  protected _clipboard?: LovelaceCardConfig;

  @state() protected _config?: StackCardConfig;

  @state() protected _selectedCard = 0;

  @state() protected _GUImode = true;

  @state() protected _guiModeAvailable? = true;

  protected _keys = new Map<string, string>();

  protected _schema: readonly HaFormSchema[] = SCHEMA;

  @query("hui-card-element-editor")
  protected _cardEditorEl?: HuiCardElementEditor;

  public setConfig(config: Readonly<StackCardConfig>): void {
    assert(config, cardConfigStruct);
    this._config = config;
  }

  public focusYamlEditor() {
    this._cardEditorEl?.focusYamlEditor();
  }

  protected formData(): object {
    return this._config!;
  }

  protected render() {
    if (!this.hass || !this._config) {
      return nothing;
    }
    const selected = this._selectedCard!;
    const numcards = this._config.cards.length;

    const isGuiMode = !this._cardEditorEl || this._GUImode;

    return html`
      <ha-form
        .hass=${this.hass}
        .data=${this.formData()}
        .schema=${this._schema}
        .computeLabel=${this._computeLabelCallback}
        @value-changed=${this._valueChanged}
      ></ha-form>
      <div class="card-config">
        <div class="toolbar">
          <ha-tab-group @wa-tab-show=${this._handleSelectedCard}>
            ${this._config.cards.map(
              (_card, i) =>
                html`<ha-tab-group-tab
                  slot="nav"
                  .panel=${i}
                  .active=${i === selected}
                >
                  ${i + 1}
                </ha-tab-group-tab>`
            )}
          </ha-tab-group>
          <ha-icon-button
            @click=${this._handleAddCard}
            .path=${mdiPlus}
          ></ha-icon-button>
        </div>

        <div id="editor">
          ${
            selected < numcards
              ? html`
                  <div id="card-options">
                    <ha-icon-button
                      class="gui-mode-button"
                      @click=${this._toggleMode}
                      .disabled=${!this._guiModeAvailable}
                      .label=${this.hass!.localize(
                        isGuiMode
                          ? "ui.panel.lovelace.editor.edit_card.show_code_editor"
                          : "ui.panel.lovelace.editor.edit_card.show_visual_editor"
                      )}
                      .path=${isGuiMode ? mdiCodeBraces : mdiListBoxOutline}
                    ></ha-icon-button>

                    <ha-icon-button-arrow-prev
                      .disabled=${selected === 0}
                      .label=${this.hass!.localize(
                        "ui.panel.lovelace.editor.edit_card.move_before"
                      )}
                      @click=${this._handleMove}
                      .move=${-1}
                    ></ha-icon-button-arrow-prev>

                    <ha-icon-button-arrow-next
                      .label=${this.hass!.localize(
                        "ui.panel.lovelace.editor.edit_card.move_after"
                      )}
                      .disabled=${selected === numcards - 1}
                      @click=${this._handleMove}
                      .move=${1}
                    ></ha-icon-button-arrow-next>

                    <ha-icon-button
                      .label=${this.hass!.localize(
                        "ui.panel.lovelace.editor.edit_card.copy"
                      )}
                      .path=${mdiContentCopy}
                      @click=${this._handleCopyCard}
                    ></ha-icon-button>

                    <ha-icon-button
                      .label=${this.hass!.localize(
                        "ui.panel.lovelace.editor.edit_card.cut"
                      )}
                      .path=${mdiContentCut}
                      @click=${this._handleCutCard}
                    ></ha-icon-button>

                    <ha-icon-button
                      .label=${this.hass!.localize(
                        "ui.panel.lovelace.editor.edit_card.delete"
                      )}
                      .path=${mdiDelete}
                      @click=${this._handleDeleteCard}
                    ></ha-icon-button>
                  </div>
                  ${keyed(
                    this._getKey(this._config.cards, selected),
                    html`<hui-card-element-editor
                      .hass=${this.hass}
                      .value=${this._config.cards[selected]}
                      .lovelace=${this.lovelace}
                      @config-changed=${this._handleConfigChanged}
                      @GUImode-changed=${this._handleGUIModeChanged}
                    ></hui-card-element-editor>`
                  )}
                `
              : html`
                  <hui-card-picker
                    .hass=${this.hass}
                    .lovelace=${this.lovelace}
                    @config-changed=${this._handleCardPicked}
                  ></hui-card-picker>
                `
          }
        </div>
      </div>
    `;
  }

  private _getKey(cards: LovelaceCardConfig[], index: number): string {
    const key = `${index}-${cards.length}`;
    if (!this._keys.has(key)) {
      this._keys.set(key, Math.random().toString());
    }

    return this._keys.get(key)!;
  }

  protected async _handleAddCard() {
    this._selectedCard = this._config!.cards.length;
  }

  protected _handleSelectedCard(ev) {
    this._GUImode = true;
    this._guiModeAvailable = true;
    this._selectedCard = parseInt(ev.detail.name, 10);
  }

  protected _handleConfigChanged(ev: HASSDomEvent<ConfigChangedEvent>) {
    ev.stopPropagation();
    if (!this._config) {
      return;
    }
    const cards = [...this._config.cards];
    const newCard = ev.detail.config as LovelaceCardConfig;
    cards[this._selectedCard] = newCard;
    this._config = { ...this._config, cards };
    this._guiModeAvailable = ev.detail.guiModeAvailable;
    fireEvent(this, "config-changed", { config: this._config });
  }

  protected _handleCardPicked(ev) {
    ev.stopPropagation();
    if (!this._config) {
      return;
    }
    const config = ev.detail.config;
    const cards = [...this._config.cards, config];
    this._config = { ...this._config, cards };
    this._keys.clear();
    fireEvent(this, "config-changed", { config: this._config });
  }

  protected _handleCopyCard() {
    if (!this._config) {
      return;
    }
    this._clipboard = deepClone(this._config.cards[this._selectedCard]);
  }

  protected _handleCutCard() {
    this._handleCopyCard();
    this._handleDeleteCard();
  }

  protected _handleDeleteCard() {
    if (!this._config) {
      return;
    }
    const cards = [...this._config.cards];
    cards.splice(this._selectedCard, 1);
    this._config = { ...this._config, cards };
    this._selectedCard = Math.max(0, this._selectedCard - 1);
    this._keys.clear();
    fireEvent(this, "config-changed", { config: this._config });
  }

  protected _handleMove(ev: Event) {
    if (!this._config) {
      return;
    }
    const move = (ev.currentTarget as any).move;
    const source = this._selectedCard;
    const target = source + move;
    const cards = [...this._config.cards];
    const card = cards.splice(this._selectedCard, 1)[0];
    cards.splice(target, 0, card);
    this._config = {
      ...this._config,
      cards,
    };
    this._selectedCard = target;
    this._keys.clear();
    fireEvent(this, "config-changed", { config: this._config });
  }

  protected _handleGUIModeChanged(ev: HASSDomEvent<GUIModeChangedEvent>): void {
    ev.stopPropagation();
    this._GUImode = ev.detail.guiMode;
    this._guiModeAvailable = ev.detail.guiModeAvailable;
  }

  protected _toggleMode(): void {
    this._cardEditorEl?.toggleMode();
  }

  protected _valueChanged(ev: CustomEvent): void {
    fireEvent(this, "config-changed", { config: ev.detail.value });
  }

  protected _computeLabelCallback = (schema: SchemaUnion<typeof SCHEMA>) =>
    this.hass!.localize(
      `ui.panel.lovelace.editor.card.${this._config!.type}.${schema.name}`
    );

  static get styles(): CSSResultGroup {
    return [
      configElementStyle,
      css`
        .toolbar {
          display: flex;
          justify-content: space-between;
          align-items: center;
        }
        ha-tab-group {
          flex-grow: 1;
          min-width: 0;
          --ha-tab-track-color: var(--card-background-color);
        }

        #card-options {
          display: flex;
          justify-content: flex-end;
          width: 100%;
        }

        #editor {
          border: 1px solid var(--divider-color);
          padding: 12px;
        }
        @media (max-width: 450px) {
          #editor {
            margin: 0 -12px;
          }
        }

        .gui-mode-button {
          margin-right: auto;
          margin-inline-end: auto;
          margin-inline-start: initial;
        }
      `,
    ];
  }
}

declare global {
  interface HTMLElementTagNameMap {
    "hui-stack-card-editor": HuiStackCardEditor;
  }
}
