import type { PropertyValues } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { consumeLocalize } from "../../common/decorators/consume-context-entry";
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";
import { unitOfMeasurementOptions } from "../../data/number";

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: {} },
    },
    {
      name: "enable_second",
      default: true,
      selector: { boolean: {} },
    },
    {
      name: "allow_negative",
      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" } },
    },
    {
      name: "unit_of_measurement",
      selector: {
        select: {
          custom_value: true,
          sort: true,
          options: unitOfMeasurementOptions,
        },
      },
    },
  ] 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;

  @consumeLocalize()
  protected _localize?: LocalizeFunc;

  private _yamlMode = false;

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

  private _schema = memoizeOne((choice: string, localize: LocalizeFunc) => {
    const schemas = SELECTOR_SCHEMAS[choice];
    return [
      {
        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)
        : []),
      ...(schemas
        ? schemas.length > 1
          ? [
              {
                name: "",
                type: "expandable",
                title: localize("ui.components.selectors.selector.options"),
                schema: schemas,
              },
            ]
          : schemas
        : []),
    ] 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._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._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;
  }
}
