import * as plugins from '../../plugins.js';
import type { IIntegrationEntity, IIntegrationEvent } from '../../core/types.js';
import type {
  INanoleafEvent,
  INanoleafPanelInfo,
  INanoleafSnapshot,
  INanoleafState,
  INanoleafValue,
} from './nanoleaf.types.js';

const RESERVED_EFFECTS = new Set(['*Solid*', '*Static*', '*Dynamic*']);

export class NanoleafMapper {
  public static toDevices(snapshotArg: INanoleafSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const devices: plugins.shxInterfaces.data.IDeviceDefinition[] = [];
    const updatedAt = new Date().toISOString();
    const controllerDeviceId = this.controllerDeviceId(snapshotArg);
    const controllerName = this.controllerName(snapshotArg);
    const features: plugins.shxInterfaces.data.IDeviceFeature[] = [
      { id: 'connectivity', capability: 'sensor', name: 'Connectivity', readable: true, writable: false },
      { id: 'on', capability: 'light', name: 'Power', readable: true, writable: true },
      { id: 'brightness', capability: 'light', name: 'Brightness', readable: true, writable: true, unit: '%' },
      { id: 'hue', capability: 'light', name: 'Hue', readable: true, writable: true },
      { id: 'saturation', capability: 'light', name: 'Saturation', readable: true, writable: true, unit: '%' },
      { id: 'color_temperature', capability: 'light', name: 'Color temperature', readable: true, writable: true, unit: 'K' },
      { id: 'color_mode', capability: 'sensor', name: 'Color mode', readable: true, writable: false },
      { id: 'effect', capability: 'light', name: 'Effect', readable: true, writable: true },
    ];
    const state: plugins.shxInterfaces.data.IDeviceState[] = [
      { featureId: 'connectivity', value: 'online', updatedAt },
      { featureId: 'on', value: this.stateValue(snapshotArg.state.on) ?? null, updatedAt },
      { featureId: 'brightness', value: this.stateValue(snapshotArg.state.brightness) ?? null, updatedAt },
      { featureId: 'hue', value: this.stateValue(snapshotArg.state.hue) ?? null, updatedAt },
      { featureId: 'saturation', value: this.stateValue(snapshotArg.state.sat) ?? null, updatedAt },
      { featureId: 'color_temperature', value: this.stateValue(snapshotArg.state.ct) ?? null, updatedAt },
      { featureId: 'color_mode', value: this.colorMode(snapshotArg.state) ?? null, updatedAt },
      { featureId: 'effect', value: this.currentEffect(snapshotArg) ?? null, updatedAt },
    ];

    if (snapshotArg.controllerInfo.firmwareVersion) {
      features.push({ id: 'firmware', capability: 'sensor', name: 'Firmware', readable: true, writable: false });
      state.push({ featureId: 'firmware', value: snapshotArg.controllerInfo.firmwareVersion, updatedAt });
    }

    const panelCount = this.panelCount(snapshotArg);
    if (panelCount !== undefined) {
      features.push({ id: 'panel_count', capability: 'sensor', name: 'Panel count', readable: true, writable: false });
      state.push({ featureId: 'panel_count', value: panelCount, updatedAt });
    }

    const rhythmConnected = this.valueLike(snapshotArg.rhythm?.rhythmConnected);
    if (rhythmConnected !== undefined) {
      features.push({ id: 'rhythm_connected', capability: 'sensor', name: 'Rhythm connected', readable: true, writable: false });
      state.push({ featureId: 'rhythm_connected', value: rhythmConnected, updatedAt });
    }

    devices.push({
      id: controllerDeviceId,
      integrationDomain: 'nanoleaf',
      name: controllerName,
      protocol: 'http',
      manufacturer: snapshotArg.controllerInfo.manufacturer || 'Nanoleaf',
      model: snapshotArg.controllerInfo.model || 'Nanoleaf Controller',
      online: true,
      features,
      state,
      metadata: {
        serialNumber: snapshotArg.controllerInfo.serialNo,
        hardwareVersion: snapshotArg.controllerInfo.hardwareVersion,
        firmwareVersion: snapshotArg.controllerInfo.firmwareVersion,
        effectsList: this.availableEffects(snapshotArg),
      },
    });

    for (const panel of this.panels(snapshotArg)) {
      devices.push(this.panelDevice(snapshotArg, panel, updatedAt));
    }

    return devices;
  }

  public static toEntities(snapshotArg: INanoleafSnapshot): IIntegrationEntity[] {
    const deviceId = this.controllerDeviceId(snapshotArg);
    const deviceSlug = this.slug(this.controllerName(snapshotArg));
    const entities: IIntegrationEntity[] = [
      {
        id: `light.${deviceSlug}`,
        uniqueId: `nanoleaf_${this.slug(snapshotArg.controllerInfo.serialNo || deviceId)}_light`,
        integrationDomain: 'nanoleaf',
        deviceId,
        platform: 'light',
        name: this.controllerName(snapshotArg),
        state: this.stateValue(snapshotArg.state.on) ? 'on' : 'off',
        attributes: {
          brightness: this.stateValue(snapshotArg.state.brightness),
          hue: this.stateValue(snapshotArg.state.hue),
          saturation: this.stateValue(snapshotArg.state.sat),
          color_temp_kelvin: this.stateValue(snapshotArg.state.ct),
          color_mode: this.colorMode(snapshotArg.state),
          effect: this.currentEffect(snapshotArg),
          effect_list: this.availableEffects(snapshotArg),
        },
        available: true,
      },
      {
        id: `button.${deviceSlug}_identify`,
        uniqueId: `nanoleaf_${this.slug(snapshotArg.controllerInfo.serialNo || deviceId)}_identify`,
        integrationDomain: 'nanoleaf',
        deviceId,
        platform: 'button',
        name: `${this.controllerName(snapshotArg)} identify`,
        state: null,
        attributes: { deviceClass: 'identify' },
        available: true,
      },
    ];

    entities.push({
      id: `select.${deviceSlug}_effect`,
      uniqueId: `nanoleaf_${this.slug(snapshotArg.controllerInfo.serialNo || deviceId)}_effect`,
      integrationDomain: 'nanoleaf',
      deviceId,
      platform: 'select',
      name: `${this.controllerName(snapshotArg)} effect`,
      state: this.currentEffect(snapshotArg) ?? null,
      attributes: { options: this.availableEffects(snapshotArg) },
      available: true,
    });

    this.pushSensorEntity(entities, deviceId, `nanoleaf_${this.slug(deviceId)}_color_mode`, `sensor.${deviceSlug}_color_mode`, `${this.controllerName(snapshotArg)} color mode`, this.colorMode(snapshotArg.state));
    this.pushSensorEntity(entities, deviceId, `nanoleaf_${this.slug(deviceId)}_firmware`, `sensor.${deviceSlug}_firmware`, `${this.controllerName(snapshotArg)} firmware`, snapshotArg.controllerInfo.firmwareVersion);
    this.pushSensorEntity(entities, deviceId, `nanoleaf_${this.slug(deviceId)}_panel_count`, `sensor.${deviceSlug}_panel_count`, `${this.controllerName(snapshotArg)} panel count`, this.panelCount(snapshotArg));
    this.pushSensorEntity(entities, deviceId, `nanoleaf_${this.slug(deviceId)}_rhythm_connected`, `sensor.${deviceSlug}_rhythm_connected`, `${this.controllerName(snapshotArg)} rhythm connected`, this.valueLike(snapshotArg.rhythm?.rhythmConnected));
    this.pushSensorEntity(entities, deviceId, `nanoleaf_${this.slug(deviceId)}_rhythm_active`, `sensor.${deviceSlug}_rhythm_active`, `${this.controllerName(snapshotArg)} rhythm active`, this.valueLike(snapshotArg.rhythm?.rhythmActive));

    for (const panel of this.panels(snapshotArg)) {
      const panelDeviceId = this.panelDeviceId(snapshotArg, panel);
      entities.push({
        id: `sensor.${deviceSlug}_panel_${panel.panelId}`,
        uniqueId: `nanoleaf_${this.slug(deviceId)}_panel_${panel.panelId}`,
        integrationDomain: 'nanoleaf',
        deviceId: panelDeviceId,
        platform: 'sensor',
        name: `${this.controllerName(snapshotArg)} panel ${panel.panelId}`,
        state: panel.panelId,
        attributes: {
          x: panel.x,
          y: panel.y,
          orientation: panel.o,
          shapeType: panel.shapeType,
        },
        available: true,
      });
    }

    return entities;
  }

  public static toIntegrationEvent(eventArg: INanoleafEvent): IIntegrationEvent {
    return {
      type: 'state_changed',
      integrationDomain: 'nanoleaf',
      data: eventArg,
      timestamp: eventArg.timestamp ?? Date.now(),
    };
  }

  private static panelDevice(snapshotArg: INanoleafSnapshot, panelArg: INanoleafPanelInfo, updatedAtArg: string): plugins.shxInterfaces.data.IDeviceDefinition {
    const features: plugins.shxInterfaces.data.IDeviceFeature[] = [
      { id: 'layout_x', capability: 'sensor', name: 'Layout X', readable: true, writable: false },
      { id: 'layout_y', capability: 'sensor', name: 'Layout Y', readable: true, writable: false },
      { id: 'orientation', capability: 'sensor', name: 'Orientation', readable: true, writable: false },
      { id: 'shape_type', capability: 'sensor', name: 'Shape type', readable: true, writable: false },
    ];
    return {
      id: this.panelDeviceId(snapshotArg, panelArg),
      integrationDomain: 'nanoleaf',
      name: `${this.controllerName(snapshotArg)} Panel ${panelArg.panelId}`,
      protocol: 'http',
      manufacturer: 'Nanoleaf',
      model: panelArg.shapeType === undefined ? 'Panel' : `Panel shape ${panelArg.shapeType}`,
      online: true,
      features,
      state: [
        { featureId: 'layout_x', value: panelArg.x ?? null, updatedAt: updatedAtArg },
        { featureId: 'layout_y', value: panelArg.y ?? null, updatedAt: updatedAtArg },
        { featureId: 'orientation', value: panelArg.o ?? null, updatedAt: updatedAtArg },
        { featureId: 'shape_type', value: panelArg.shapeType ?? null, updatedAt: updatedAtArg },
      ],
      metadata: {
        panelId: panelArg.panelId,
        controllerDeviceId: this.controllerDeviceId(snapshotArg),
      },
    };
  }

  private static pushSensorEntity(
    entitiesArg: IIntegrationEntity[],
    deviceIdArg: string,
    uniqueIdArg: string,
    idArg: string,
    nameArg: string,
    valueArg: unknown
  ): void {
    if (valueArg === undefined) {
      return;
    }
    entitiesArg.push({
      id: idArg,
      uniqueId: uniqueIdArg,
      integrationDomain: 'nanoleaf',
      deviceId: deviceIdArg,
      platform: 'sensor',
      name: nameArg,
      state: valueArg,
      available: true,
    });
  }

  private static controllerDeviceId(snapshotArg: INanoleafSnapshot): string {
    return `nanoleaf.controller.${this.slug(snapshotArg.controllerInfo.serialNo || snapshotArg.controllerInfo.name || 'unknown')}`;
  }

  private static panelDeviceId(snapshotArg: INanoleafSnapshot, panelArg: INanoleafPanelInfo): string {
    return `nanoleaf.panel.${this.slug(snapshotArg.controllerInfo.serialNo || snapshotArg.controllerInfo.name || 'unknown')}.${panelArg.panelId}`;
  }

  private static controllerName(snapshotArg: INanoleafSnapshot): string {
    return snapshotArg.controllerInfo.name || snapshotArg.controllerInfo.model || 'Nanoleaf';
  }

  private static panels(snapshotArg: INanoleafSnapshot): INanoleafPanelInfo[] {
    return snapshotArg.panelLayout?.layout?.positionData ?? [];
  }

  private static panelCount(snapshotArg: INanoleafSnapshot): number | undefined {
    return snapshotArg.panelLayout?.layout?.numPanels ?? snapshotArg.panelLayout?.layout?.positionData?.length;
  }

  private static currentEffect(snapshotArg: INanoleafSnapshot): string | undefined {
    const effect = snapshotArg.effects.select;
    if (!effect || RESERVED_EFFECTS.has(effect)) {
      return undefined;
    }
    return effect;
  }

  private static availableEffects(snapshotArg: INanoleafSnapshot): string[] {
    return (snapshotArg.effects.effectsList ?? []).filter((effectArg) => !RESERVED_EFFECTS.has(effectArg));
  }

  private static colorMode(stateArg: INanoleafState): string | undefined {
    return this.valueLike(stateArg.colorMode);
  }

  private static stateValue<TValue>(valueArg: INanoleafValue<TValue> | undefined): TValue | undefined {
    return valueArg?.value;
  }

  private static valueLike<TValue>(valueArg: INanoleafValue<TValue> | TValue | undefined): TValue | undefined {
    if (this.isRecord(valueArg) && 'value' in valueArg) {
      return valueArg.value as TValue;
    }
    return valueArg as TValue | undefined;
  }

  private static slug(valueArg: string): string {
    return valueArg.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '') || 'nanoleaf';
  }

  private static isRecord(valueArg: unknown): valueArg is Record<string, unknown> {
    return typeof valueArg === 'object' && valueArg !== null && !Array.isArray(valueArg);
  }
}
