import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { refine } from "superstruct";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/ha-assist-pipeline-picker";
import type {
  HaFormSchema,
  SchemaUnion,
} from "../../../components/ha-form/types";
import "../../../components/ha-help-tooltip";
import "../../../components/ha-navigation-picker";
import type { HaSelectSelectEvent } from "../../../components/ha-select";
import "../../../components/ha-service-control";
import "../../../components/input/ha-input";
import type { HaInput } from "../../../components/input/ha-input";
import type {
  ActionConfig,
  CallServiceActionConfig,
  NavigateActionConfig,
  UrlActionConfig,
} from "../../../data/lovelace/config/action";
import type { ServiceAction } from "../../../data/script";
import type { HomeAssistant } from "../../../types";
import { canToggleState } from "../../../common/entity/can_toggle_state";
import { canToggleDomain } from "../../../common/entity/can_toggle_domain";
import { computeDomain } from "../../../common/entity/compute_domain";

export type UiAction = Exclude<ActionConfig["action"], "fire-dom-event">;

export interface ActionRelatedContext {
  entity_id?: string;
  area_id?: string;
}

export const ACTION_RELATED_CONTEXT = {
  entity_id: "entity",
  area_id: "area",
} as const satisfies HaFormSchema["context"] & ActionRelatedContext;

const DEFAULT_ACTIONS: UiAction[] = [
  "more-info",
  "toggle",
  "navigate",
  "url",
  "perform-action",
  "assist",
  "none",
];

export const supportedActions = (struct: any, supported_actions: UiAction[]) =>
  refine(struct, supported_actions.toString(), (value: any) =>
    supported_actions.includes(value.action)
  );

const ASSIST_SCHEMA = [
  {
    type: "grid",
    name: "",
    schema: [
      {
        name: "pipeline_id",
        selector: {
          assist_pipeline: {
            include_last_used: true,
          },
        },
      },
      {
        name: "start_listening",
        selector: {
          boolean: {},
        },
      },
    ],
  },
] as const satisfies readonly HaFormSchema[];

@customElement("hui-action-editor")
export class HuiActionEditor extends LitElement {
  @property({ attribute: false }) public config?: ActionConfig;

  @property({ attribute: false }) public label?: string;

  @property({ attribute: false }) public actions?: UiAction[];

  @property({ attribute: false }) public defaultAction?: UiAction;

  @property({ attribute: false }) public tooltipText?: string;

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

  @property({ type: Boolean }) public required?: boolean;

  @property({ attribute: false })
  public context?: ActionRelatedContext;

  get _navigation_path(): string {
    const config = this.config as NavigateActionConfig | undefined;
    return config?.navigation_path || "";
  }

  get _url_path(): string {
    const config = this.config as UrlActionConfig | undefined;
    return config?.url_path || "";
  }

  get _service(): string {
    const config = this.config as CallServiceActionConfig;
    return config?.perform_action || config?.service || "";
  }

  private _serviceAction = memoizeOne(
    (config: CallServiceActionConfig): ServiceAction => ({
      action: this._service,
      ...(config.data || config.service_data
        ? { data: config.data ?? config.service_data }
        : null),
      target: config.target,
    })
  );

  private _navigateSchema = memoizeOne(
    (
      relatedEntityId?: string,
      relatedAreaId?: string
    ): readonly HaFormSchema[] => [
      {
        name: "navigation_path",
        selector: {
          navigation: {
            ...(relatedEntityId ? { entity_id: relatedEntityId } : {}),
            ...(relatedAreaId ? { area_id: relatedAreaId } : {}),
          },
        },
      },
    ]
  );

  private _filterToggleAction = memoizeOne(
    (actions: UiAction[]): UiAction[] => {
      return actions.filter((a) => a !== "toggle");
    }
  );

  protected render() {
    if (!this.hass) {
      return nothing;
    }

    let action = this.config?.action || (this.required ? "" : "default");

    let actions = this.actions ?? DEFAULT_ACTIONS;

    if (
      this.context?.entity_id &&
      action !== "toggle" &&
      actions.includes("toggle")
    ) {
      const stateObj = this.hass.states[this.context.entity_id];
      const canToggle = stateObj
        ? canToggleState(this.hass, stateObj)
        : canToggleDomain(this.hass, computeDomain(this.context.entity_id));
      if (!canToggle) {
        actions = this._filterToggleAction(actions);
      }
    }

    if (action === "call-service") {
      action = "perform-action";
    }

    const defaultOption = {
      value: "default",
      label: `${this.hass!.localize(
        "ui.panel.lovelace.editor.action-editor.actions.default_action"
      )} ${
        this.defaultAction
          ? ` (${this.hass!.localize(
              `ui.panel.lovelace.editor.action-editor.actions.${this.defaultAction}`
            )})`
          : ""
      }`,
    };

    return html`
      <div class="dropdown">
        <ha-select
          .label=${this.label}
          .configValue=${"action"}
          @selected=${this._actionPicked}
          .value=${action}
          .options=${[
            ...(this.required ? [] : [defaultOption]),
            ...actions.map((actn) => ({
              value: actn,
              label: this.hass!.localize(
                `ui.panel.lovelace.editor.action-editor.actions.${actn}`
              ),
            })),
          ]}
        >
        </ha-select>
        ${
          this.tooltipText
            ? html`
                <ha-help-tooltip .label=${this.tooltipText}></ha-help-tooltip>
              `
            : nothing
        }
      </div>
      ${
        this.config?.action === "navigate"
          ? html`
              <ha-form
                .hass=${this.hass}
                .schema=${this._navigateSchema(
                  this.context?.entity_id,
                  this.context?.area_id
                )}
                .data=${this.config}
                .computeLabel=${this._computeFormLabel}
                @value-changed=${this._formValueChanged}
              >
              </ha-form>
            `
          : nothing
      }
      ${
        this.config?.action === "url"
          ? html`
              <ha-input
                .label=${this.hass!.localize(
                  "ui.panel.lovelace.editor.action-editor.url_path"
                )}
                .value=${this._url_path}
                .configValue=${"url_path"}
                @input=${this._valueChanged}
              ></ha-input>
            `
          : nothing
      }
      ${
        this.config?.action === "call-service" ||
        this.config?.action === "perform-action"
          ? html`
              <ha-service-control
                .hass=${this.hass}
                .value=${this._serviceAction(this.config)}
                narrow
                @value-changed=${this._serviceValueChanged}
              ></ha-service-control>
            `
          : nothing
      }
      ${
        this.config?.action === "assist"
          ? html`
              <ha-form
                .hass=${this.hass}
                .schema=${ASSIST_SCHEMA}
                .data=${this.config}
                .computeLabel=${this._computeFormLabel}
                @value-changed=${this._formValueChanged}
              >
              </ha-form>
            `
          : nothing
      }
    `;
  }

  private _actionPicked(ev: HaSelectSelectEvent): void {
    ev.stopPropagation();
    if (!this.hass) {
      return;
    }
    let action = this.config?.action;

    if (action === "call-service") {
      action = "perform-action";
    }

    const value = ev.detail.value;

    if (action === value) {
      return;
    }
    if (value === "default" || !value) {
      fireEvent(this, "value-changed", { value: undefined });
      return;
    }

    let data;
    switch (value) {
      case "url": {
        data = { url_path: this._url_path };
        break;
      }
      case "perform-action": {
        data = { perform_action: this._service };
        break;
      }
      case "navigate": {
        data = { navigation_path: this._navigation_path };
        break;
      }
    }

    fireEvent(this, "value-changed", {
      value: { action: value, ...data },
    });
  }

  private _valueChanged(
    ev: InputEvent & { target: HaInput & { configValue?: string } }
  ): void {
    ev.stopPropagation();
    if (!this.hass) {
      return;
    }
    const { configValue } = ev.target;
    const value = ev.target.value ?? "";
    if (this[`_${configValue}`] === value) {
      return;
    }
    if (configValue) {
      fireEvent(this, "value-changed", {
        value: { ...this.config!, [configValue]: value },
      });
    }
  }

  private _formValueChanged(ev): void {
    ev.stopPropagation();
    const value = ev.detail.value;

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

  private _computeFormLabel(schema: SchemaUnion<typeof ASSIST_SCHEMA>) {
    return this.hass?.localize(
      `ui.panel.lovelace.editor.action-editor.${schema.name}`
    );
  }

  private _serviceValueChanged(ev: CustomEvent) {
    ev.stopPropagation();
    const value = {
      ...this.config!,
      action: "perform-action",
      perform_action: ev.detail.value.action || "",
      data: ev.detail.value.data,
      target: ev.detail.value.target || {},
    };
    if (!ev.detail.value.data) {
      delete value.data;
    }
    // "service_data" is allowed for backwards compatibility but replaced with "data" on write
    if ("service_data" in value) {
      delete value.service_data;
    }
    if ("service" in value) {
      delete value.service;
    }

    fireEvent(this, "value-changed", { value });
  }

  static styles = css`
    .dropdown {
      position: relative;
    }
    ha-help-tooltip {
      position: absolute;
      right: 40px;
      top: 16px;
      inset-inline-start: initial;
      inset-inline-end: 40px;
      direction: var(--direction);
    }
    ha-select,
    ha-input {
      width: 100%;
    }
    ha-service-control,
    ha-navigation-picker,
    ha-form {
      display: block;
    }
    ha-input,
    ha-service-control,
    ha-navigation-picker,
    ha-form {
      margin-top: 8px;
    }
    ha-service-control {
      --service-control-padding: 0;
    }
  `;
}

declare global {
  interface HTMLElementTagNameMap {
    "hui-action-editor": HuiActionEditor;
  }
}
