import type * as shxInterfaces from '@smarthome.exchange/interfaces';
import { SimpleLocalMapper, type IIntegrationEntity, type ISimpleLocalEntitySnapshot, type ISimpleLocalSnapshot, type ISimpleLocalSnapshotOptions, type TEntityPlatform, type TSimpleLocalRawData } from '../../core/index.js';
import type { IEmulatedHueConfig } from './emulated_hue.types.js';
import { emulatedHueDefaultName, emulatedHueDefaultPort, emulatedHueProfile, emulatedHueSerialNumber, emulatedHueUuid } from './emulated_hue.types.js';

export class EmulatedHueMapper {
  public static toSnapshot(optionsArg: Omit<ISimpleLocalSnapshotOptions<IEmulatedHueConfig>, 'profile'>): ISimpleLocalSnapshot {
    return SimpleLocalMapper.toSnapshot({
      ...optionsArg,
      profile: emulatedHueProfile,
      rawData: this.normalizeRawData(optionsArg.config, optionsArg.rawData),
    });
  }

  public static toSnapshotFromRaw(configArg: IEmulatedHueConfig, rawDataArg: TSimpleLocalRawData): ISimpleLocalSnapshot {
    return this.toSnapshot({ config: configArg, rawData: rawDataArg, online: true, source: 'manual' });
  }

  public static toDevices(snapshotArg: ISimpleLocalSnapshot): shxInterfaces.data.IDeviceDefinition[] {
    return SimpleLocalMapper.toDevices(emulatedHueProfile, snapshotArg);
  }

  public static toEntities(snapshotArg: ISimpleLocalSnapshot): IIntegrationEntity[] {
    return SimpleLocalMapper.toEntities(emulatedHueProfile, snapshotArg);
  }

  public static slug(valueArg: unknown): string {
    return SimpleLocalMapper.slug(valueArg);
  }

  private static normalizeRawData(configArg: IEmulatedHueConfig, rawDataArg: unknown): unknown {
    if (!isRecord(rawDataArg) || isSnapshotLike(rawDataArg) || hasSimpleEntities(rawDataArg)) {
      return rawDataArg;
    }

    const hueLights = hueLightEntities(rawDataArg.lights);
    const haStates = stateRecords(rawDataArg.states ?? rawDataArg.hassStates ?? rawDataArg.entities);
    const entities = haStates.length ? entitiesFromStates(configArg, rawDataArg, haStates) : hueLights;
    if (!entities.length) {
      return rawDataArg;
    }

    const config = recordValue(rawDataArg.config) || {};
    const endpoint = endpointInfo(configArg, rawDataArg, config);
    const name = configArg.name || stringValue(config.name) || stringValue(rawDataArg.name) || 'HASS BRIDGE';

    return {
      device: {
        id: configArg.uniqueId || stringValue(config.mac) || emulatedHueSerialNumber,
        name,
        manufacturer: emulatedHueProfile.manufacturer,
        model: emulatedHueProfile.model,
        serialNumber: emulatedHueSerialNumber,
        host: endpoint.host,
        port: endpoint.port,
        protocol: emulatedHueProfile.defaultProtocol,
        configurationUrl: endpoint.host ? `http://${endpoint.host}:${endpoint.port || emulatedHueDefaultPort}` : undefined,
        attributes: {
          uuid: emulatedHueUuid,
          type: configArg.type || stringValue(rawDataArg.type) || 'google_home',
          exposeByDefault: configArg.exposeByDefault ?? configArg.expose_by_default ?? rawDataArg.expose_by_default ?? rawDataArg.exposeByDefault,
          lightsAllDimmable: configArg.lightsAllDimmable ?? configArg.lights_all_dimmable ?? rawDataArg.lights_all_dimmable ?? rawDataArg.lightsAllDimmable,
        },
      },
      entities,
      online: configArg.online ?? true,
      updatedAt: new Date().toISOString(),
      source: 'manual',
      rawData: rawDataArg,
    } satisfies ISimpleLocalSnapshot;
  }
}

const defaultExposedDomains = ['switch', 'light', 'group', 'input_boolean', 'media_player', 'fan'];
const platformByDomain: Record<string, TEntityPlatform> = {
  climate: 'climate',
  cover: 'cover',
  fan: 'fan',
  group: 'switch',
  humidifier: 'fan',
  input_boolean: 'switch',
  light: 'light',
  media_player: 'media_player',
  scene: 'button',
  script: 'button',
  switch: 'switch',
};
const offStates = new Set(['closed', 'off', 'unavailable', 'unknown']);

const entitiesFromStates = (configArg: IEmulatedHueConfig, rawDataArg: Record<string, unknown>, stateRecordsArg: Array<Record<string, unknown>>): ISimpleLocalEntitySnapshot[] => {
  const exposedDomains = stringArray(configArg.exposedDomains) || stringArray(configArg.exposed_domains) || stringArray(rawDataArg.exposed_domains) || stringArray(rawDataArg.exposedDomains) || defaultExposedDomains;
  const exposeByDefault = configArg.exposeByDefault ?? configArg.expose_by_default ?? rawDataArg.expose_by_default ?? rawDataArg.exposeByDefault ?? true;
  return stateRecordsArg.map((stateArg, indexArg) => entityFromState(configArg, exposedDomains, Boolean(exposeByDefault), stateArg, indexArg)).filter((entityArg): entityArg is ISimpleLocalEntitySnapshot => Boolean(entityArg));
};

const entityFromState = (configArg: IEmulatedHueConfig, exposedDomainsArg: string[], exposeByDefaultArg: boolean, stateArg: Record<string, unknown>, indexArg: number): ISimpleLocalEntitySnapshot | undefined => {
  const entityId = stringValue(stateArg.entity_id) || stringValue(stateArg.entityId) || stringValue(stateArg.id);
  if (!entityId) {
    return undefined;
  }
  const domain = entityId.split('.')[0];
  const platform = platformByDomain[domain];
  if (!platform || (exposeByDefaultArg && !exposedDomainsArg.includes(domain)) || (!exposeByDefaultArg && !configArg.exposedEntities?.[entityId])) {
    return undefined;
  }

  const attributes = recordValue(stateArg.attributes) || {};
  const exposedEntityConfig = configArg.exposedEntities?.[entityId];
  if (exposedEntityConfig?.hidden) {
    return undefined;
  }

  return {
    id: SimpleLocalMapper.slug(entityId),
    uniqueId: `${emulatedHueProfile.domain}_${SimpleLocalMapper.slug(entityId)}`,
    name: exposedEntityConfig?.name || stringValue(attributes.emulated_hue_name) || stringValue(attributes.friendly_name) || stringValue(stateArg.name) || entityId,
    platform,
    state: hueStateValue(domain, stateArg.state),
    available: stringValue(stateArg.state) !== 'unavailable',
    writable: true,
    attributes: {
      entityId,
      hueNumber: indexArg + 1,
      hueReachable: stringValue(stateArg.state) !== 'unavailable',
      brightness: attributes.brightness,
      colorTempKelvin: attributes.color_temp_kelvin,
      hsColor: attributes.hs_color,
      supportedColorModes: attributes.supported_color_modes,
      supportedFeatures: attributes.supported_features,
    },
  };
};

const hueLightEntities = (valueArg: unknown): ISimpleLocalEntitySnapshot[] => {
  if (!isRecord(valueArg)) {
    return [];
  }
  return Object.entries(valueArg).filter((entryArg): entryArg is [string, Record<string, unknown>] => isRecord(entryArg[1])).map(([numberArg, lightArg]) => {
    const state = recordValue(lightArg.state) || {};
    return {
      id: `hue_${SimpleLocalMapper.slug(numberArg)}`,
      uniqueId: `${emulatedHueProfile.domain}_${SimpleLocalMapper.slug(stringValue(lightArg.uniqueid) || numberArg)}`,
      name: stringValue(lightArg.name) || `Hue ${numberArg}`,
      platform: 'light',
      state: state.on === true,
      available: state.reachable !== false,
      writable: true,
      attributes: {
        hueNumber: numberArg,
        hueUniqueId: lightArg.uniqueid,
        hueType: lightArg.type,
        modelId: lightArg.modelid,
        brightness: state.bri,
        colorMode: state.colormode,
      },
    };
  });
};

const endpointInfo = (configArg: IEmulatedHueConfig, rawDataArg: Record<string, unknown>, hueConfigArg: Record<string, unknown>): { host?: string; port?: number } => {
  const ipAddress = stringValue(hueConfigArg.ipaddress) || stringValue(rawDataArg.ipaddress);
  const parsed = parseHostPort(ipAddress);
  const host = configArg.host || configArg.hostIp || configArg.host_ip || stringValue(rawDataArg.host_ip) || stringValue(rawDataArg.hostIp) || parsed?.host;
  const port = configArg.port || configArg.listenPort || configArg.listen_port || numberValue(rawDataArg.listen_port) || numberValue(rawDataArg.listenPort) || parsed?.port || (host ? emulatedHueDefaultPort : undefined);
  return { host, port };
};

const parseHostPort = (valueArg?: string): { host: string; port?: number } | undefined => {
  if (!valueArg) {
    return undefined;
  }
  const [host, port] = valueArg.split(':');
  return host ? { host, port: port ? Number(port) : undefined } : undefined;
};

const stateRecords = (valueArg: unknown): Array<Record<string, unknown>> => {
  if (Array.isArray(valueArg)) {
    return valueArg.filter(isRecord);
  }
  if (isRecord(valueArg)) {
    return Object.values(valueArg).filter(isRecord);
  }
  return [];
};

const hueStateValue = (domainArg: string, valueArg: unknown): boolean | string => {
  const value = stringValue(valueArg);
  if (!value) {
    return false;
  }
  if (domainArg === 'cover') {
    return value !== 'closed';
  }
  return !offStates.has(value);
};

const hasSimpleEntities = (valueArg: Record<string, unknown>): boolean => Array.isArray(valueArg.entities) && valueArg.entities.some((entityArg) => isRecord(entityArg) && 'name' in entityArg && 'state' in entityArg && !('entity_id' in entityArg));
const isSnapshotLike = (valueArg: Record<string, unknown>): boolean => isRecord(valueArg.device) && Array.isArray(valueArg.entities);
const recordValue = (valueArg: unknown): Record<string, unknown> | undefined => isRecord(valueArg) ? valueArg : undefined;
const isRecord = (valueArg: unknown): valueArg is Record<string, unknown> => Boolean(valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg));
const stringValue = (valueArg: unknown): string | undefined => typeof valueArg === 'string' && valueArg.trim() ? valueArg.trim() : undefined;
const stringArray = (valueArg: unknown): string[] | undefined => Array.isArray(valueArg) ? valueArg.filter((itemArg): itemArg is string => typeof itemArg === 'string') : undefined;
const numberValue = (valueArg: unknown): number | undefined => {
  const value = typeof valueArg === 'number' ? valueArg : typeof valueArg === 'string' ? Number(valueArg) : undefined;
  return value !== undefined && Number.isFinite(value) ? value : undefined;
};
