import { ReactiveElement } from "lit";
import { customElement } from "lit/decorators";
import memoizeOne from "memoize-one";
import { getAreasFloorHierarchy } from "../../../common/areas/areas-floor-hierarchy";
import {
  findEntities,
  generateEntityFilter,
  type EntityFilter,
} from "../../../common/entity/entity_filter";
import { floorDefaultIcon } from "../../../components/ha-floor-icon";
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
import type { LovelaceSectionRawConfig } from "../../../data/lovelace/config/section";
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
import type { HomeAssistant } from "../../../types";
import type { TileCardConfig } from "../../lovelace/cards/types";
import { BINARY_STATE_ON } from "../../../common/const";
import { computeDomain } from "../../../common/entity/compute_domain";
import {
  type EntityRegistryDisplayEntry,
  findBatteryChargingEntity,
} from "../../../data/entity/entity_registry";

export interface MaintenanceViewStrategyConfig {
  type: "maintenance";
}

export const maintenanceEntityFilters: EntityFilter[] = [
  {
    domain: "sensor",
    device_class: ["battery"],
  },
  {
    domain: "binary_sensor",
    device_class: ["battery"],
  },
];

const LOW_BATTERY_THRESHOLD = 20;

const _deviceEntities = memoizeOne(
  (
    deviceId: string,
    entities: HomeAssistant["entities"]
  ): EntityRegistryDisplayEntry[] => {
    const entries = Object.values(entities);
    return entries.filter((entity) => entity.device_id === deviceId);
  }
);

export const filterLowBatteryEntities = (
  hass: HomeAssistant,
  entityIds: string[]
): string[] =>
  entityIds.filter((entityId) => {
    const state = hass.states[entityId]?.state ?? "";
    if (computeDomain(entityId) === "binary_sensor") {
      return state === BINARY_STATE_ON;
    }

    const deviceId = hass.entities[entityId]?.device_id;
    const entities = deviceId ? _deviceEntities(deviceId, hass.entities) : [];

    const batteryChargingEntity = findBatteryChargingEntity(
      hass.states,
      entities
    );
    const batteryCharging = batteryChargingEntity
      ? hass.states[batteryChargingEntity?.entity_id]
      : undefined;

    if (batteryCharging && batteryCharging.state === "on") {
      return false;
    }

    const stateValue = parseFloat(state);
    return !isNaN(stateValue) && stateValue <= LOW_BATTERY_THRESHOLD;
  });

export const filterUnavailableBatteryEntities = (
  hass: HomeAssistant,
  entityIds: string[]
): string[] =>
  entityIds.filter((entityId) => {
    return hass.states[entityId]?.state === "unavailable";
  });

const computeBatteryTileCard = (
  entities: HomeAssistant["entities"],
  entityId: string
): TileCardConfig => {
  const entity = entities[entityId];
  const deviceId = entity?.device_id;
  return {
    type: "tile",
    entity: entityId,
    name: { type: deviceId ? "device" : "entity" },
  };
};

const processAreasForBattery = (
  areaIds: string[],
  hass: HomeAssistant,
  entities: string[]
): LovelaceCardConfig[] => {
  const cards: LovelaceCardConfig[] = [];

  for (const areaId of areaIds) {
    const area = hass.areas[areaId];
    if (!area) continue;

    const areaFilter = generateEntityFilter(hass, {
      area: area.area_id,
    });
    const areaBatteryEntities = entities.filter(areaFilter);
    const areaCards: LovelaceCardConfig[] = [];

    for (const entityId of areaBatteryEntities) {
      areaCards.push(computeBatteryTileCard(hass.entities, entityId));
    }

    if (areaCards.length > 0) {
      cards.push({
        heading_style: "subtitle",
        type: "heading",
        heading: area.name,
        tap_action: hass.panels.home
          ? {
              action: "navigate",
              navigation_path: `/home/areas-${area.area_id}`,
            }
          : undefined,
      });
      cards.push(...areaCards);
    }
  }

  return cards;
};

const processUnassignedEntities = (
  hass: HomeAssistant,
  entities: string[]
): LovelaceCardConfig[] => {
  const unassignedFilter = generateEntityFilter(hass, {
    area: null,
  });
  const unassignedEntities = entities.filter(unassignedFilter);
  const cards: LovelaceCardConfig[] = [];

  for (const entityId of unassignedEntities) {
    cards.push(computeBatteryTileCard(hass.entities, entityId));
  }

  return cards;
};

@customElement("maintenance-view-strategy")
export class MaintenanceViewStrategy extends ReactiveElement {
  static async generate(
    _config: MaintenanceViewStrategyConfig,
    hass: HomeAssistant
  ): Promise<LovelaceViewConfig> {
    const areas = Object.values(hass.areas);
    const floors = Object.values(hass.floors);
    const hierarchy = getAreasFloorHierarchy(floors, areas);

    const sections: LovelaceSectionRawConfig[] = [];

    const allEntities = Object.keys(hass.states);

    const batteryFilters = maintenanceEntityFilters.map((filter) =>
      generateEntityFilter(hass, filter)
    );

    const entities = findEntities(allEntities, batteryFilters);

    const floorCount =
      hierarchy.floors.length + (hierarchy.areas.length ? 1 : 0);

    // Process floors
    for (const floorStructure of hierarchy.floors) {
      const floorId = floorStructure.id;
      const areaIds = floorStructure.areas;
      const floor = hass.floors[floorId];

      const section: LovelaceSectionRawConfig = {
        type: "grid",
        column_span: 2,
        cards: [
          {
            type: "heading",
            heading:
              floorCount > 1
                ? floor.name
                : hass.localize("ui.panel.lovelace.strategy.home.areas"),
            icon: floor.icon || floorDefaultIcon(floor),
          },
        ],
      };

      const areaCards = processAreasForBattery(areaIds, hass, entities);

      if (areaCards.length > 0) {
        section.cards!.push(...areaCards);
        sections.push(section);
      }
    }

    // Process unassigned areas
    if (hierarchy.areas.length > 0) {
      const section: LovelaceSectionRawConfig = {
        type: "grid",
        column_span: 2,
        cards: [
          {
            type: "heading",
            heading:
              floorCount > 1
                ? hass.localize(
                    "ui.panel.lovelace.strategy.maintenance.other_devices"
                  )
                : hass.localize("ui.panel.lovelace.strategy.home.areas"),
          },
        ],
      };

      const areaCards = processAreasForBattery(hierarchy.areas, hass, entities);

      if (areaCards.length > 0) {
        section.cards!.push(...areaCards);
        sections.push(section);
      }
    }

    // Process unassigned entities
    const unassignedCards = processUnassignedEntities(hass, entities);

    if (unassignedCards.length > 0) {
      const section: LovelaceSectionRawConfig = {
        type: "grid",
        column_span: 2,
        cards: [
          {
            type: "heading",
            heading:
              sections.length > 0
                ? hass.localize(
                    "ui.panel.lovelace.strategy.maintenance.other_devices"
                  )
                : hass.localize(
                    "ui.panel.lovelace.strategy.maintenance.devices"
                  ),
          },
          ...unassignedCards,
        ],
      };
      sections.push(section);
    }

    return {
      type: "sections",
      max_columns: 2,
      sections: sections,
    };
  }
}

declare global {
  interface HTMLElementTagNameMap {
    "maintenance-view-strategy": MaintenanceViewStrategy;
  }
}
