import "@home-assistant/webawesome/dist/components/divider/divider";
import { consume } from "@lit/context";
import {
  mdiContentCopy,
  mdiContentCut,
  mdiContentDuplicate,
  mdiDelete,
  mdiDotsVertical,
  mdiFlask,
  mdiPlaylistEdit,
} from "@mdi/js";
import deepClone from "deep-clone-simple";
import type { PropertyValues } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { ConditionListenersController } from "../../../../common/controllers/condition-listeners-controller";
import { storage } from "../../../../common/decorators/storage";
import { dynamicElement } from "../../../../common/dom/dynamic-element-directive";
import { fireEvent } from "../../../../common/dom/fire_event";
import { stopPropagation } from "../../../../common/dom/stop_propagation";
import { handleStructError } from "../../../../common/structs/handle-errors";
import "../../../../components/automation/ha-automation-row-event-chip";
import "../../../../components/automation/ha-automation-row-live-test";
import type { LiveTestState } from "../../../../components/automation/ha-automation-row-live-test";
import "../../../../components/ha-alert";
import "../../../../components/ha-card";
import "../../../../components/ha-dropdown";
import type { HaDropdownSelectEvent } from "../../../../components/ha-dropdown";
import "../../../../components/ha-dropdown-item";
import "../../../../components/ha-expansion-panel";
import "../../../../components/ha-icon-button";
import "../../../../components/ha-svg-icon";
import "../../../../components/ha-tooltip";
import "../../../../components/ha-yaml-editor";
import { showAlertDialog } from "../../../../dialogs/generic/show-dialog-box";
import { haStyle } from "../../../../resources/styles";
import type { HomeAssistant } from "../../../../types";
import { ICON_CONDITION } from "../../common/icon-condition";
import type {
  AndCondition,
  Condition,
  LegacyCondition,
  NotCondition,
  OrCondition,
} from "../../common/validate-condition";
import {
  checkConditionsMet,
  validateConditionalConfig,
} from "../../common/validate-condition";
import type { ConditionsEntityContext } from "./context";
import { conditionsEntityContext } from "./context";
import type { LovelaceConditionEditorConstructor } from "./types";

const NO_ENTITY_CONDITIONS = ["state", "numeric_state"];

const CONTAINER_CONDITIONS = ["and", "or", "not"];

const isNoEntityCondition = (condition: string, noEntity: boolean): boolean =>
  NO_ENTITY_CONDITIONS.includes(condition) && noEntity;

export const getConditionClassName = (condition: string, noEntity: boolean) => {
  if (isNoEntityCondition(condition, noEntity)) {
    return `ha-card-condition-${condition}-no_entity`;
  }
  return `ha-card-condition-${condition}`;
};

const containsNoEntityCondition = (
  condition: Condition,
  noEntity: boolean
): boolean =>
  noEntity &&
  CONTAINER_CONDITIONS.includes(condition.condition) &&
  (condition as OrCondition | AndCondition | NotCondition).conditions?.some(
    (c) => NO_ENTITY_CONDITIONS.includes(c.condition)
  ) === true;

@customElement("ha-card-condition-editor")
export class HaCardConditionEditor extends LitElement {
  @property({ attribute: false }) public hass!: HomeAssistant;

  @property({ attribute: false }) condition!: Condition | LegacyCondition;

  @state()
  @consume({ context: conditionsEntityContext, subscribe: true })
  private _entityContext?: ConditionsEntityContext;

  private get _noEntity(): boolean {
    return this._entityContext?.mode === "filter";
  }

  @storage({
    key: "dashboardConditionClipboard",
    state: false,
    subscribe: false,
    storage: "sessionStorage",
  })
  protected _clipboard?: Condition | LegacyCondition;

  @state() public _yamlMode = false;

  @state() public _uiAvailable = false;

  @state() public _uiWarnings: string[] = [];

  @state() _condition?: Condition;

  @state() private _testingResult?: boolean;

  @state() private _liveTestResult: {
    state: LiveTestState;
    message?: string;
  } = { state: "unknown" };

  private _listeners = new ConditionListenersController(this);

  private get _editor() {
    if (!this._condition) return undefined;
    return customElements.get(
      getConditionClassName(this._condition.condition, this._noEntity)
    ) as LovelaceConditionEditorConstructor | undefined;
  }

  public expand() {
    this.updateComplete.then(() => {
      this.shadowRoot!.querySelector("ha-expansion-panel")!.expanded = true;
    });
  }

  private _setupConditionListeners() {
    this._listeners.setup(
      this.condition ? [this.condition as Condition] : [],
      this.hass,
      () => this._evaluateLiveTest()
    );
  }

  protected willUpdate(changedProperties: PropertyValues<this>): void {
    if (changedProperties.has("condition")) {
      this._condition = {
        condition: "state",
        ...this.condition,
      };
      const validator = this._editor?.validateUIConfig;
      if (validator) {
        try {
          validator(this._condition, this.hass);
          this._uiAvailable = true;
          this._uiWarnings = [];
        } catch (err) {
          this._uiWarnings = handleStructError(
            this.hass,
            err as Error
          ).warnings;
          this._uiAvailable = false;
        }
      } else {
        this._uiAvailable = false;
        this._uiWarnings = [];
      }

      if (!this._uiAvailable && !this._yamlMode) {
        this._yamlMode = true;
      }

      this._setupConditionListeners();
    }

    if (changedProperties.has("condition") || changedProperties.has("hass")) {
      this._evaluateLiveTest();
    }
  }

  protected updated(changedProperties: PropertyValues<this>): void {
    if ((changedProperties as Map<string, unknown>).has("_entityContext")) {
      this._evaluateLiveTest();
    }
  }

  private _evaluateLiveTest() {
    if (!this.condition || !this._condition) {
      this._liveTestResult = { state: "unknown" };
      return;
    }

    if (
      isNoEntityCondition(this._condition.condition, this._noEntity) ||
      containsNoEntityCondition(this._condition, this._noEntity)
    ) {
      this._liveTestResult = {
        state: "unknown",
        message: this.hass.localize(
          "ui.panel.lovelace.editor.condition-editor.live_test_state.unknown"
        ),
      };
      return;
    }

    if (!validateConditionalConfig([this.condition])) {
      this._liveTestResult = {
        state: "invalid",
        message: this.hass.localize(
          "ui.panel.lovelace.editor.condition-editor.live_test_state.invalid"
        ),
      };
      return;
    }

    const testContext =
      this._entityContext?.mode === "current"
        ? { entity_id: this._entityContext.entityId }
        : {};
    const pass = checkConditionsMet([this.condition], this.hass, testContext);
    this._liveTestResult = {
      state: pass ? "pass" : "fail",
      message: this.hass.localize(
        `ui.panel.lovelace.editor.condition-editor.live_test_state.${pass ? "pass" : "fail"}`
      ),
    };
  }

  protected render() {
    const condition = this._condition;

    if (!condition) return nothing;

    const hideLiveTest =
      isNoEntityCondition(condition.condition, this._noEntity) ||
      containsNoEntityCondition(condition, this._noEntity);

    return html`
      <div class="container">
        <ha-expansion-panel left-chevron>
          <div
            id="condition-icon"
            class="icon-badge-wrapper"
            slot="leading-icon"
          >
            <ha-svg-icon
              .path=${ICON_CONDITION[condition.condition]}
            ></ha-svg-icon>
            ${
              hideLiveTest
                ? nothing
                : html`<ha-automation-row-live-test
                    .state=${this._liveTestResult.state}
                    .label=${this.hass.localize(
                      `ui.panel.lovelace.editor.condition-editor.live_test_state.${this._liveTestResult.state}`
                    )}
                  ></ha-automation-row-live-test>`
            }
          </div>
          ${
            !hideLiveTest && this._liveTestResult.message
              ? html`<ha-tooltip for="condition-icon" slot="leading-icon"
                  >${this._liveTestResult.message}</ha-tooltip
                >`
              : nothing
          }
          <h3 slot="header">
            ${
              this.hass.localize(
                `ui.panel.lovelace.editor.condition-editor.condition.${condition.condition}.label`
              ) || condition.condition
            }
          </h3>
          <ha-automation-row-event-chip
            .show=${this._testingResult !== undefined}
            .variant=${this._testingResult ? "success" : "warning"}
            slot="event"
            class="event-chip"
            aria-live="polite"
          >
            ${
              this._testingResult
                ? this.hass.localize(
                    "ui.panel.lovelace.editor.condition-editor.testing_pass"
                  )
                : this.hass.localize(
                    "ui.panel.lovelace.editor.condition-editor.testing_error"
                  )
            }
          </ha-automation-row-event-chip>
          <ha-dropdown
            slot="icons"
            @wa-select=${this._handleAction}
            @click=${stopPropagation}
            placement="bottom-end"
          >
            <ha-icon-button
              slot="trigger"
              .label=${this.hass.localize("ui.common.menu")}
              .path=${mdiDotsVertical}
            >
            </ha-icon-button>

            ${
              isNoEntityCondition(condition.condition, this._noEntity) ||
              containsNoEntityCondition(condition, this._noEntity)
                ? nothing
                : html`<ha-dropdown-item value="test">
                    ${this.hass.localize(
                      "ui.panel.lovelace.editor.condition-editor.test"
                    )}
                    <ha-svg-icon slot="icon" .path=${mdiFlask}></ha-svg-icon>
                  </ha-dropdown-item>`
            }

            <ha-dropdown-item value="duplicate">
              ${this.hass.localize(
                "ui.panel.lovelace.editor.edit_card.duplicate"
              )}
              <ha-svg-icon
                slot="icon"
                .path=${mdiContentDuplicate}
              ></ha-svg-icon>
            </ha-dropdown-item>

            <ha-dropdown-item value="copy">
              ${this.hass.localize("ui.panel.lovelace.editor.edit_card.copy")}
              <ha-svg-icon slot="icon" .path=${mdiContentCopy}></ha-svg-icon>
            </ha-dropdown-item>

            <ha-dropdown-item value="cut">
              ${this.hass.localize("ui.panel.lovelace.editor.edit_card.cut")}
              <ha-svg-icon slot="icon" .path=${mdiContentCut}></ha-svg-icon>
            </ha-dropdown-item>

            <ha-dropdown-item
              value="toggle_yaml"
              .disabled=${!this._uiAvailable}
            >
              ${this.hass.localize(
                `ui.panel.lovelace.editor.edit_view.edit_${!this._yamlMode ? "yaml" : "ui"}`
              )}
              <ha-svg-icon slot="icon" .path=${mdiPlaylistEdit}></ha-svg-icon>
            </ha-dropdown-item>

            <wa-divider></wa-divider>

            <ha-dropdown-item variant="danger" value="delete">
              ${this.hass!.localize("ui.common.delete")}
              <ha-svg-icon slot="icon" .path=${mdiDelete}></ha-svg-icon>
            </ha-dropdown-item>
          </ha-dropdown>
          ${
            !this._uiAvailable
              ? html`
                  <ha-alert
                    alert-type="warning"
                    .title=${this.hass.localize(
                      "ui.errors.config.editor_not_supported"
                    )}
                  >
                    ${
                      this._uiWarnings!.length > 0 &&
                      this._uiWarnings![0] !== undefined
                        ? html`
                            <ul>
                              ${this._uiWarnings!.map(
                                (warning) => html`<li>${warning}</li>`
                              )}
                            </ul>
                          `
                        : nothing
                    }
                    ${this.hass.localize(
                      "ui.errors.config.edit_in_yaml_supported"
                    )}
                  </ha-alert>
                `
              : nothing
          }
          <div class="content">
            ${
              this._yamlMode
                ? html`
                    <ha-yaml-editor
                      .defaultValue=${this.condition}
                      @value-changed=${this._onYamlChange}
                    ></ha-yaml-editor>
                  `
                : html`
                    ${dynamicElement(
                      getConditionClassName(
                        condition.condition,
                        this._noEntity
                      ),
                      {
                        hass: this.hass,
                        condition: condition,
                      }
                    )}
                  `
            }
          </div>
        </ha-expansion-panel>
      </div>
    `;
  }

  private async _handleAction(ev: HaDropdownSelectEvent) {
    const action = ev.detail.item.value;

    if (action === undefined) {
      return;
    }

    switch (action) {
      case "test":
        await this._testCondition();
        return;
      case "duplicate":
        this._duplicateCondition();
        return;
      case "copy":
        this._copyCondition();
        return;
      case "cut":
        this._cutCondition();
        return;
      case "toggle_yaml":
        this._yamlMode = !this._yamlMode;
        return;
      case "delete":
        this._delete();
    }
  }

  private _timeout?: number;

  private async _testCondition() {
    if (this._timeout) {
      window.clearTimeout(this._timeout);
      this._timeout = undefined;
    }
    this._testingResult = undefined;
    const condition = this.condition;

    const validateResult = validateConditionalConfig([this.condition]);

    if (!validateResult) {
      showAlertDialog(this, {
        title: this.hass.localize(
          "ui.panel.lovelace.editor.condition-editor.invalid_config_title"
        ),
        text: this.hass.localize(
          "ui.panel.lovelace.editor.condition-editor.invalid_config_text"
        ),
      });
      return;
    }

    const testContext =
      this._entityContext?.mode === "current"
        ? { entity_id: this._entityContext.entityId }
        : {};
    this._testingResult = checkConditionsMet(
      [condition],
      this.hass,
      testContext
    );

    this._timeout = window.setTimeout(() => {
      this._testingResult = undefined;
    }, 2500);
  }

  private _duplicateCondition() {
    fireEvent(this, "duplicate-condition", {
      value: deepClone(this.condition),
    });
  }

  private _copyCondition() {
    this._clipboard = deepClone(this.condition);
  }

  private _cutCondition() {
    this._copyCondition();
    this._delete();
  }

  private _delete = () => {
    fireEvent(this, "value-changed", { value: null });
  };

  private _onYamlChange(ev: CustomEvent) {
    ev.stopPropagation();
    if (!ev.detail.isValid) {
      return;
    }
    // @ts-ignore
    fireEvent(this, "value-changed", { value: ev.detail.value });
  }

  static styles = [
    haStyle,
    css`
      ha-dropdown {
        --mdc-theme-text-primary-on-background: var(--primary-text-color);
      }
      ha-expansion-panel {
        --expansion-panel-summary-padding: 0 0 0 8px;
        --expansion-panel-content-padding: 0;
      }
      .icon-badge-wrapper {
        display: inline-flex;
        position: relative;
        color: var(--secondary-text-color);
        opacity: 0.9;
      }
      h3 {
        margin: 0;
        font-size: inherit;
        font-weight: inherit;
      }
      .content {
        padding: 12px;
      }
      .disabled {
        opacity: 0.5;
        pointer-events: none;
      }
      .event-chip {
        position: absolute;
        inset-inline-end: 40px;
      }
      .container {
        position: relative;
        border-radius: var(--ha-card-border-radius, var(--ha-border-radius-lg));
        border: 1px solid var(--divider-color);
      }
    `,
  ];
}

declare global {
  interface HTMLElementTagNameMap {
    "ha-card-condition-editor": HaCardConditionEditor;
  }

  interface HASSDomEvents {
    "duplicate-condition": { value: Condition | LegacyCondition };
  }
}
