import type { HassEntity } from "home-assistant-js-websocket";
import type { HomeAssistant } from "../../types";
import { ensureArray } from "../array/ensure-array";
import { computeRTL } from "../util/compute_rtl";
import { computeAreaName } from "./compute_area_name";
import { computeDeviceName } from "./compute_device_name";
import { computeEntityName, entityUseDeviceName } from "./compute_entity_name";
import { computeFloorName } from "./compute_floor_name";
import { computeStateName } from "./compute_state_name";
import { getEntityContext } from "./context/get_entity_context";

const DEFAULT_SEPARATOR = " ";

export const DEFAULT_ENTITY_NAME = [
  { type: "device" },
  { type: "entity" },
] satisfies EntityNameItem[];

export type EntityNameItem =
  | {
      type: "entity" | "device" | "area" | "floor";
    }
  | {
      type: "text";
      text: string;
    };

export interface EntityNameOptions {
  separator?: string;
}

export const computeEntityNameDisplay = (
  stateObj: HassEntity,
  name: string | EntityNameItem | EntityNameItem[] | undefined,
  entities: HomeAssistant["entities"],
  devices: HomeAssistant["devices"],
  areas: HomeAssistant["areas"],
  floors: HomeAssistant["floors"],
  options?: EntityNameOptions
) => {
  if (typeof name === "string") {
    return name;
  }

  // If no name config is provided, fall back to the friendly name
  if (!name) {
    return computeStateName(stateObj);
  }

  let items = ensureArray(name);

  const separator = options?.separator ?? DEFAULT_SEPARATOR;

  // If all items are text, just join them
  if (items.every((n) => n.type === "text")) {
    return items.map((item) => item.text).join(separator);
  }

  const useDeviceName = entityUseDeviceName(stateObj, entities, devices);

  // If entity uses device name, and device is not already included, replace it with device name
  if (useDeviceName) {
    const hasDevice = items.some((n) => n.type === "device");
    if (!hasDevice) {
      items = items.map((n) => (n.type === "entity" ? { type: "device" } : n));
    }
  }

  const names = computeEntityNameList(
    stateObj,
    items,
    entities,
    devices,
    areas,
    floors
  );

  // If after processing there is only one name, return that
  if (names.length === 1) {
    return names[0] || "";
  }

  return names.filter((n) => n).join(separator);
};

export const computeEntityNameList = (
  stateObj: HassEntity,
  name: EntityNameItem[],
  entities: HomeAssistant["entities"],
  devices: HomeAssistant["devices"],
  areas: HomeAssistant["areas"],
  floors: HomeAssistant["floors"]
): (string | undefined)[] => {
  const { device, area, floor } = getEntityContext(
    stateObj,
    entities,
    devices,
    areas,
    floors
  );

  const names = name.map((item) => {
    switch (item.type) {
      case "entity":
        return computeEntityName(stateObj, entities, devices);
      case "device":
        return device ? computeDeviceName(device) : undefined;
      case "area":
        return area ? computeAreaName(area) : undefined;
      case "floor":
        return floor ? computeFloorName(floor) : undefined;
      case "text":
        return item.text;
      default:
        return "";
    }
  });

  return names;
};

export interface EntityPickerDisplay {
  primary: string;
  secondary?: string;
}

export const computeEntityPickerDisplay = (
  hass: Pick<
    HomeAssistant,
    | "entities"
    | "devices"
    | "areas"
    | "floors"
    | "language"
    | "translationMetadata"
  >,
  stateObj: HassEntity
): EntityPickerDisplay => {
  const [entityName, deviceName, areaName] = computeEntityNameList(
    stateObj,
    [{ type: "entity" }, { type: "device" }, { type: "area" }],
    hass.entities,
    hass.devices,
    hass.areas,
    hass.floors
  );

  const isRTL = computeRTL(
    hass.language,
    hass.translationMetadata.translations
  );

  const primary = entityName || deviceName || stateObj.entity_id;
  const secondary =
    [areaName, entityName ? deviceName : undefined]
      .filter(Boolean)
      .join(isRTL ? " ◂ " : " ▸ ") || undefined;

  return { primary, secondary };
};
