import type * as shxInterfaces from '@smarthome.exchange/interfaces';
import { SimpleLocalMapper, type IIntegrationEntity, type ISimpleLocalEntitySnapshot, type ISimpleLocalSnapshot, type ISimpleLocalSnapshotOptions, type TSimpleLocalRawData } from '../../core/index.js';
import type { IHdmiCecConfig, THdmiCecPlatform } from './hdmi_cec.types.js';
import { hdmiCecProfile } from './hdmi_cec.types.js';

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

  public static toSnapshotFromRaw(configArg: IHdmiCecConfig, 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(hdmiCecProfile, snapshotArg);
  }

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

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

  private static normalizeRawData(configArg: IHdmiCecConfig, rawDataArg: unknown): unknown {
    if (!isRecord(rawDataArg) || ('device' in rawDataArg && 'entities' in rawDataArg)) {
      return rawDataArg;
    }

    const deviceRecords = recordsValue(rawDataArg.devices) || recordsValue(recordValue(rawDataArg.network)?.devices) || (isCecDevice(rawDataArg) ? [rawDataArg] : undefined);
    if (!deviceRecords?.length) {
      return rawDataArg;
    }

    const host = configArg.host || stringValue(rawDataArg.host);
    const name = configArg.name || stringValue(rawDataArg.name) || hdmiCecProfile.defaultName;
    return {
      device: {
        id: configArg.uniqueId || host || hdmiCecProfile.domain,
        name,
        manufacturer: 'HDMI-CEC',
        model: host ? 'pyCEC TCP adapter' : 'CEC adapter',
        host,
        port: configArg.port,
        protocol: host ? 'tcp' : 'local',
        attributes: {
          osdName: configArg.osdName,
          aliases: configArg.devices,
        },
      },
      entities: deviceRecords.map((recordArg, indexArg) => cecEntity(configArg, recordArg, indexArg)),
      online: configArg.online ?? true,
      updatedAt: new Date().toISOString(),
      source: 'manual',
      rawData: rawDataArg,
    } satisfies ISimpleLocalSnapshot;
  }
}

const cecEntity = (configArg: IHdmiCecConfig, recordArg: Record<string, unknown>, indexArg: number): ISimpleLocalEntitySnapshot => {
  const logicalAddress = numberValue(recordArg.logicalAddress ?? recordArg.logical_address ?? recordArg.logical) ?? indexArg;
  const entityKey = stringValue(recordArg.entityId) || stringValue(recordArg.entity_id) || `hdmi_${logicalAddress.toString(16)}`;
  const platform = cecPlatform(configArg, recordArg, entityKey);
  const power = cecPower(recordArg.powerStatus ?? recordArg.power_status ?? recordArg.power ?? recordArg.isOn ?? recordArg.is_on);
  const state = platform === 'switch' ? (power ?? false) : cecMediaState(recordArg, power);
  return {
    id: entityKey,
    name: cecName(recordArg, logicalAddress),
    platform,
    state,
    writable: true,
    attributes: {
      logicalAddress,
      physicalAddress: stringValue(recordArg.physicalAddress ?? recordArg.physical_address),
      type: stringValue(recordArg.typeName ?? recordArg.type_name ?? recordArg.type),
      typeId: numberValue(recordArg.typeId ?? recordArg.type_id),
      vendorName: stringValue(recordArg.vendorName ?? recordArg.vendor_name ?? recordArg.vendor),
      vendorId: recordArg.vendorId ?? recordArg.vendor_id,
    },
  };
};

const cecPlatform = (configArg: IHdmiCecConfig, recordArg: Record<string, unknown>, entityKeyArg: string): THdmiCecPlatform => {
  const configured = configArg.types?.[entityKeyArg] || configArg.platform;
  const rawPlatform = stringValue(recordArg.platform);
  if (configured === 'switch' || configured === 'media_player') {
    return configured;
  }
  if (rawPlatform === 'switch' || rawPlatform === 'media_player') {
    return rawPlatform;
  }
  return 'media_player';
};

const cecName = (recordArg: Record<string, unknown>, logicalAddressArg: number): string => {
  const vendor = stringValue(recordArg.vendorName ?? recordArg.vendor_name ?? recordArg.vendor);
  const osdName = stringValue(recordArg.osdName ?? recordArg.osd_name ?? recordArg.name);
  const typeName = stringValue(recordArg.typeName ?? recordArg.type_name ?? recordArg.type);
  if (vendor && vendor !== 'Unknown' && osdName) {
    return `${vendor} ${osdName}`;
  }
  return osdName || (typeName ? `${typeName} ${logicalAddressArg}` : `HDMI ${logicalAddressArg}`);
};

const cecMediaState = (recordArg: Record<string, unknown>, powerArg: boolean | undefined): string => {
  const state = stringValue(recordArg.state)?.toLowerCase();
  const status = stringValue(recordArg.status)?.toLowerCase();
  if (powerArg === false || state === 'off') {
    return 'off';
  }
  if (state && state !== 'on') {
    return state;
  }
  if (status === 'play' || status === 'playing') {
    return 'playing';
  }
  if (status === 'pause' || status === 'paused' || status === 'still') {
    return 'paused';
  }
  if (status === 'stop' || status === 'stopped') {
    return 'idle';
  }
  return powerArg ? 'on' : 'unknown';
};

const cecPower = (valueArg: unknown): boolean | undefined => {
  if (typeof valueArg === 'boolean') {
    return valueArg;
  }
  if (typeof valueArg === 'number') {
    if ([0, 3].includes(valueArg)) {
      return false;
    }
    if ([1, 4].includes(valueArg)) {
      return true;
    }
  }
  const value = stringValue(valueArg)?.toLowerCase();
  if (!value) {
    return undefined;
  }
  if (['on', '1', 'true', 'powered_on'].includes(value)) {
    return true;
  }
  if (['off', 'standby', '0', 'false', 'powered_off'].includes(value)) {
    return false;
  }
  return undefined;
};

const isCecDevice = (valueArg: Record<string, unknown>): boolean => 'logicalAddress' in valueArg || 'logical_address' in valueArg || 'physicalAddress' in valueArg || 'physical_address' in valueArg;

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

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 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;
};
