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 { IEmulatedRokuConfig } from './emulated_roku.types.js';
import { emulatedRokuDefaultName, emulatedRokuProfile } from './emulated_roku.types.js';

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

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

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

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

  private static normalizeRawData(configArg: IEmulatedRokuConfig, rawDataArg: unknown): unknown {
    if (isSimpleSnapshot(rawDataArg)) {
      return rawDataArg;
    }

    const servers = serverEntries(configArg, rawDataArg);
    if (!servers.length) {
      return rawDataArg;
    }

    const rawRecord = recordValue(rawDataArg);
    const firstServer = servers[0];
    const host = configArg.host || stringValue(rawRecord?.host_ip) || stringValue(rawRecord?.hostIp) || stringValue(firstServer.hostIp);
    const listenPort = configArg.listenPort || configArg.port || firstServer.listenPort || emulatedRokuProfile.defaultPort;
    const name = configArg.name || stringValue(rawRecord?.name) || firstServer.name || emulatedRokuDefaultName;
    const entities = servers.map((serverArg) => serverEntity(serverArg));

    return {
      device: {
        id: configArg.uniqueId || host || name,
        name,
        manufacturer: emulatedRokuProfile.manufacturer,
        model: emulatedRokuProfile.model,
        host,
        port: listenPort,
        protocol: emulatedRokuProfile.defaultProtocol,
        attributes: {
          serverCount: servers.length,
          eventType: 'roku_command',
        },
      },
      entities,
      online: configArg.online ?? true,
      updatedAt: new Date().toISOString(),
      source: 'manual',
      rawData: rawDataArg,
    } satisfies ISimpleLocalSnapshot;
  }
}

interface IServerRecord {
  id: string;
  name: string;
  hostIp?: string;
  listenPort?: number;
  advertiseIp?: string;
  advertisePort?: number;
  upnpBindMulticast?: boolean;
  state: string;
}

const serverEntity = (serverArg: IServerRecord): ISimpleLocalEntitySnapshot => {
  const slug = SimpleLocalMapper.slug(serverArg.id || serverArg.name);
  return {
    id: `${slug}_server`,
    uniqueId: `${emulatedRokuProfile.domain}_${slug}_server`,
    name: `${serverArg.name} Server`,
    platform: 'sensor',
    state: serverArg.state,
    available: true,
    writable: false,
    attributes: {
      hostIp: serverArg.hostIp,
      listenPort: serverArg.listenPort,
      advertiseIp: serverArg.advertiseIp,
      advertisePort: serverArg.advertisePort,
      upnpBindMulticast: serverArg.upnpBindMulticast,
      eventType: 'roku_command',
    },
  };
};

const serverEntries = (configArg: IEmulatedRokuConfig, valueArg: unknown): IServerRecord[] => {
  const rawRecord = recordValue(valueArg);
  const nested = rawRecord?.servers ?? rawRecord?.server;
  const records = Array.isArray(nested) ? nested : nested ? [nested] : rawRecord ? [rawRecord] : [];
  const servers = records.map((entryArg, indexArg) => toServerRecord(configArg, entryArg, indexArg)).filter((entryArg): entryArg is IServerRecord => Boolean(entryArg));
  if (servers.length) {
    return servers;
  }
  if (configArg.name || configArg.host || configArg.listenPort || configArg.port) {
    return [{
      id: configArg.uniqueId || configArg.name || configArg.host || emulatedRokuDefaultName,
      name: configArg.name || emulatedRokuDefaultName,
      hostIp: configArg.host,
      listenPort: configArg.listenPort || configArg.port || emulatedRokuProfile.defaultPort,
      advertiseIp: configArg.advertiseIp,
      advertisePort: configArg.advertisePort,
      upnpBindMulticast: configArg.upnpBindMulticast,
      state: configArg.online === false ? 'stopped' : 'configured',
    }];
  }
  return [];
};

const toServerRecord = (configArg: IEmulatedRokuConfig, valueArg: unknown, indexArg: number): IServerRecord | undefined => {
  const record = recordValue(valueArg);
  if (!record) {
    return undefined;
  }
  const name = stringValue(record.name) || (indexArg === 0 ? configArg.name : undefined) || `${emulatedRokuDefaultName} ${indexArg + 1}`;
  const listenPort = numberValue(record.listen_port) || numberValue(record.listenPort) || numberValue(record.port) || (indexArg === 0 ? configArg.listenPort || configArg.port : undefined) || emulatedRokuProfile.defaultPort;
  const hostIp = stringValue(record.host_ip) || stringValue(record.hostIp) || stringValue(record.host) || (indexArg === 0 ? configArg.host : undefined);
  return {
    id: stringValue(record.id) || `${name}:${listenPort}`,
    name,
    hostIp,
    listenPort,
    advertiseIp: stringValue(record.advertise_ip) || stringValue(record.advertiseIp) || (indexArg === 0 ? configArg.advertiseIp : undefined),
    advertisePort: numberValue(record.advertise_port) || numberValue(record.advertisePort) || (indexArg === 0 ? configArg.advertisePort : undefined),
    upnpBindMulticast: booleanValue(record.upnp_bind_multicast) ?? booleanValue(record.upnpBindMulticast) ?? (indexArg === 0 ? configArg.upnpBindMulticast : undefined),
    state: stringValue(record.state) || 'configured',
  };
};

const isSimpleSnapshot = (valueArg: unknown): valueArg is ISimpleLocalSnapshot => Boolean(recordValue(valueArg)?.device && Array.isArray(recordValue(valueArg)?.entities));
const recordValue = (valueArg: unknown): Record<string, unknown> | undefined => valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg) ? valueArg as Record<string, unknown> : undefined;
const stringValue = (valueArg: unknown): string | undefined => typeof valueArg === 'string' && valueArg.trim() ? valueArg.trim() : undefined;
const numberValue = (valueArg: unknown): number | undefined => {
  if (typeof valueArg === 'number' && Number.isFinite(valueArg)) {
    return Math.round(valueArg);
  }
  if (typeof valueArg === 'string' && valueArg.trim() && Number.isFinite(Number(valueArg))) {
    return Math.round(Number(valueArg));
  }
  return undefined;
};
const booleanValue = (valueArg: unknown): boolean | undefined => typeof valueArg === 'boolean' ? valueArg : undefined;
