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 { IEnigma2Config } from './enigma2.types.js';
import { enigma2DefaultName, enigma2DefaultPort, enigma2Profile } from './enigma2.types.js';

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

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

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

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

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

    const status = recordValue(rawDataArg.status) || recordValue(rawDataArg.data) || rawDataArg;
    const currentService = recordValue(status.currservice) || recordValue(status.currentService) || recordValue(status.current_service) || {};
    const hasStatus = 'in_standby' in status || 'inStandby' in status || 'volume' in status || 'muted' in status || 'state' in status || Object.keys(currentService).length > 0;
    if (!hasStatus) {
      return rawDataArg;
    }

    const about = recordValue(rawDataArg.about) || rawDataArg;
    const info = recordValue(about.info) || recordValue(rawDataArg.info) || about;
    const iface = firstRecord(info.ifaces) || firstRecord(info.interfaces);
    const host = configArg.host || stringValue(rawDataArg.host) || stringValue(info.host);
    const port = configArg.port || numberValue(rawDataArg.port) || (host ? enigma2DefaultPort : undefined);
    const mac = configArg.macAddress || configArg.mac_address || stringValue(rawDataArg.mac) || stringValue(rawDataArg.macAddress) || stringValue(iface?.mac);
    const name = configArg.name || stringValue(rawDataArg.name) || stringValue(status.name) || enigma2DefaultName;
    const inStandby = booleanValue(status.in_standby ?? status.inStandby ?? status.standby);
    const mediaState = stringValue(status.state) || (inStandby === undefined ? 'unknown' : inStandby ? 'off' : 'on');
    const station = stringValue(currentService.station);
    const seriesTitle = stringValue(currentService.name);
    const volume = numberValue(status.volume);
    const entities: ISimpleLocalEntitySnapshot[] = [{
      id: 'media_player',
      uniqueId: `${enigma2Profile.domain}_${SimpleLocalMapper.slug(mac || host || name)}_media_player`,
      name,
      platform: 'media_player',
      state: mediaState,
      available: configArg.online ?? true,
      writable: true,
      attributes: {
        mediaTitle: station || seriesTitle,
        mediaSeriesTitle: seriesTitle,
        mediaChannel: station,
        mediaContentId: stringValue(currentService.serviceref) || stringValue(currentService.serviceRef),
        mediaDescription: stringValue(currentService.fulldescription) || stringValue(currentService.description),
        mediaStartTime: currentService.begin,
        mediaEndTime: currentService.end,
        mediaCurrentlyRecording: booleanValue(status.is_recording ?? status.isRecording),
        isVolumeMuted: booleanValue(status.muted),
        volumeLevel: volume === undefined ? undefined : volume / 100,
        sourceList: arrayValue(status.source_list) || arrayValue(status.sourceList) || arrayValue(rawDataArg.source_list) || arrayValue(rawDataArg.sourceList),
        sourceBouquet: configArg.sourceBouquet || configArg.source_bouquet,
        useChannelIcon: configArg.useChannelIcon ?? configArg.use_channel_icon,
        deepStandby: configArg.deepStandby ?? configArg.deep_standby,
      },
    }];

    return {
      device: {
        id: configArg.uniqueId || mac || (host ? `${host}:${port || ''}` : undefined) || name,
        name,
        manufacturer: stringValue(info.brand) || enigma2Profile.manufacturer,
        model: stringValue(info.model) || enigma2Profile.model,
        serialNumber: mac,
        host,
        port,
        protocol: configArg.useTls || configArg.ssl ? 'https' : enigma2Profile.defaultProtocol,
        attributes: {
          mac,
          verifySsl: configArg.verifySsl ?? configArg.verify_ssl,
        },
      },
      entities,
      online: configArg.online ?? true,
      updatedAt: new Date().toISOString(),
      source: 'manual',
      rawData: rawDataArg,
    } satisfies ISimpleLocalSnapshot;
  }
}

const recordValue = (valueArg: unknown): Record<string, unknown> | undefined => isRecord(valueArg) ? valueArg : undefined;

const firstRecord = (valueArg: unknown): Record<string, unknown> | undefined => Array.isArray(valueArg) ? valueArg.find(isRecord) : 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;
};

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

const arrayValue = (valueArg: unknown): unknown[] | undefined => Array.isArray(valueArg) ? valueArg : undefined;
