import type { PropertyValues } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../common/dom/fire_event";
import type {
  LocalizeFunc,
  LocalizeKeys,
} from "../../common/translations/localize";
import type { HomeAssistant } from "../../types";
import "../ha-form/ha-form";

const SELECTOR_DEFAULTS = {
  number: {
    min: 1,
    max: 100,
  },
};

const SELECTOR_SCHEMAS = {
  action: [] as const,
  area: [
    {
      name: "multiple",
      selector: { boolean: {} },
    },
  ] as const,
  attribute: [
    {
      name: "entity_id",
      selector: { entity: {} },
    },
  ] as const,
  boolean: [] as const,
  color_temp: [
    {
      name: "unit",
      selector: { select: { options: ["kelvin", "mired"] } },
    },
    {
      name: "min",
      selector: { number: { mode: "box" } },
    },
    {
      name: "max",
      selector: { number: { mode: "box" } },
    },
  ] as const,
  condition: [] as const,
  date: [] as const,
  datetime: [] as const,
  device: [
    {
      name: "multiple",
      selector: { boolean: {} },
    },
  ] as const,
  duration: [
    {
      name: "enable_day",
      selector: { boolean: {} },
    },
    {
      name: "enable_millisecond",
      selector: { boolean: {} },
    },
  ] as const,
  entity: [
    {
      name: "multiple",
      selector: { boolean: {} },
    },
  ] as const,
  floor: [
    {
      name: "multiple",
      selector: { boolean: {} },
    },
  ] as const,
  icon: [] as const,
  location: [] as const,
  media: [
    {
      name: "accept",
      selector: {
        text: {
          multiple: true,
        },
      },
    },
  ] as const,
  number: [
    {
      name: "min",
      selector: { number: { mode: "box", step: "any" } },
    },
    {
      name: "max",
      selector: { number: { mode: "box", step: "any" } },
    },
    {
      name: "step",
      selector: { number: { mode: "box", step: "any" } },
    },
  ] as const,
  object: [] as const,
  color_rgb: [] as const,
  select: [
    {
      name: "options",
      selector: { object: {} },
    },
    {
      name: "multiple",
      selector: { boolean: {} },
    },
  ] as const,
  state: [
    {
      name: "entity_id",
      selector: { entity: {} },
    },
    {
      name: "multiple",
      selector: { boolean: {} },
    },
  ] as const,
  target: [] as const,
  template: [] as const,
  text: [
    {
      name: "multiple",
      selector: { boolean: {} },
    },
    {
      name: "multiline",
      selector: { boolean: {} },
    },
    { name: "prefix", selector: { text: {} } },
    { name: "suffix", selector: { text: {} } },
  ] as const,
  theme: [] as const,
  time: [] as const,
};

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

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

  @property() public label?: string;

  @property() public helper?: string;

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

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

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

  private _yamlMode = false;

  protected shouldUpdate(changedProps: PropertyValues) {
    if (changedProps.size === 1 && changedProps.has("hass")) {
      return false;
    }
    return true;
  }

  private _schema = memoizeOne(
    (choice: string, localize: LocalizeFunc) =>
      [
        {
          name: "type",
          required: true,
          selector: {
            select: {
              mode: "dropdown",
              options: Object.keys(SELECTOR_SCHEMAS)
                .concat("manual")
                .map((key) => ({
                  label:
                    localize(
                      `ui.components.selectors.selector.types.${key}` as LocalizeKeys
                    ) || key,
                  value: key,
                })),
            },
          },
        },
        ...(choice === "manual"
          ? ([
              {
                name: "manual",
                selector: { object: {} },
              },
            ] as const)
          : []),
        ...(SELECTOR_SCHEMAS[choice]
          ? SELECTOR_SCHEMAS[choice].length > 1
            ? [
                {
                  name: "",
                  type: "expandable",
                  title: localize("ui.components.selectors.selector.options"),
                  schema: SELECTOR_SCHEMAS[choice],
                },
              ]
            : SELECTOR_SCHEMAS[choice]
          : []),
      ] as const
  );

  protected render() {
    let data;
    let type;
    if (this._yamlMode) {
      type = "manual";
      data = { type, manual: this.value };
    } else {
      type = Object.keys(this.value)[0];
      const value0 = Object.values(this.value)[0];
      data = {
        type,
        ...(typeof value0 === "object" ? value0 : []),
      };
    }

    const schema = this._schema(type, this.hass.localize);

    return html`<div>
      <p>${this.label ? this.label : ""}</p>
      <ha-form
        .hass=${this.hass}
        .data=${data}
        .schema=${schema}
        .computeLabel=${this._computeLabelCallback}
        @value-changed=${this._valueChanged}
        .narrow=${this.narrow}
      ></ha-form>
    </div>`;
  }

  private _valueChanged(ev: CustomEvent) {
    ev.stopPropagation();
    const value = ev.detail.value;

    const type = value.type;
    if (!type || typeof value !== "object" || Object.keys(value).length === 0) {
      // not sure how this happens, but reject it
      return;
    }

    const oldType = Object.keys(this.value)[0];
    if (type === "manual" && !this._yamlMode) {
      this._yamlMode = true;
      this.requestUpdate();
      return;
    }
    if (type === "manual" && value.manual === undefined) {
      return;
    }
    if (type !== "manual") {
      this._yamlMode = false;
    }
    delete value.type;

    let newValue;
    if (type === "manual") {
      newValue = value.manual;
    } else if (type === oldType) {
      newValue = {
        [type]: { ...(value.manual ? value.manual[oldType] : value) },
      };
    } else {
      newValue = { [type]: { ...SELECTOR_DEFAULTS[type] } };
    }

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

  private _computeLabelCallback = (schema: any): string =>
    this.hass.localize(
      `ui.components.selectors.selector.${schema.name}` as LocalizeKeys
    ) || schema.name;

  static styles = css`
    .title {
      font-size: var(--ha-font-size-l);
      padding-top: 16px;
      overflow: hidden;
      text-overflow: ellipsis;
      margin-bottom: 16px;
      padding-left: 16px;
      padding-right: 4px;
      padding-inline-start: 16px;
      padding-inline-end: 4px;
      white-space: nowrap;
    }
  `;
}

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