import * as plugins from '../../plugins.js';
import type { IIntegrationEntity, IServiceCallRequest, TEntityPlatform } from '../../core/types.js';
import type {
  ISynologyDsmActionDescriptor,
  ISynologyDsmCameraInfo,
  ISynologyDsmCommand,
  ISynologyDsmConfig,
  ISynologyDsmDiskInfo,
  ISynologyDsmEvent,
  ISynologyDsmNetworkInfo,
  ISynologyDsmSnapshot,
  ISynologyDsmStorageInfo,
  ISynologyDsmSwitchInfo,
  ISynologyDsmSystemInfo,
  ISynologyDsmUpdateInfo,
  ISynologyDsmUtilizationInfo,
  ISynologyDsmVolumeInfo,
  TSynologyDsmCommandAction,
} from './synology_dsm.types.js';
import { synologyDsmDefaultPort, synologyDsmDefaultSsl, synologyDsmDefaultSslPort, synologyDsmDomain } from './synology_dsm.types.js';

type TSynologyDsmSensorDescriptor<TSource extends Record<string, unknown>> = {
  sourceKey: keyof TSource & string;
  nativeKey: string;
  name: string;
  unit?: string;
  deviceClass?: string;
  stateClass?: string;
  entityCategory?: string;
  enabledByDefault?: boolean;
  transform?: (valueArg: unknown) => unknown;
};

const manufacturer = 'Synology';

const utilizationSensorDescriptors: TSynologyDsmSensorDescriptor<ISynologyDsmUtilizationInfo>[] = [
  { sourceKey: 'cpuOtherLoad', nativeKey: 'cpu_other_load', name: 'CPU other load', unit: '%', stateClass: 'measurement', enabledByDefault: false },
  { sourceKey: 'cpuUserLoad', nativeKey: 'cpu_user_load', name: 'CPU user load', unit: '%', stateClass: 'measurement' },
  { sourceKey: 'cpuSystemLoad', nativeKey: 'cpu_system_load', name: 'CPU system load', unit: '%', stateClass: 'measurement', enabledByDefault: false },
  { sourceKey: 'cpuTotalLoad', nativeKey: 'cpu_total_load', name: 'CPU total load', unit: '%', stateClass: 'measurement' },
  { sourceKey: 'cpu1MinLoad', nativeKey: 'cpu_1min_load', name: 'CPU 1 minute load', unit: 'load', enabledByDefault: false, transform: (valueArg) => SynologyDsmMapper.loadValue(valueArg) },
  { sourceKey: 'cpu5MinLoad', nativeKey: 'cpu_5min_load', name: 'CPU 5 minute load', unit: 'load', transform: (valueArg) => SynologyDsmMapper.loadValue(valueArg) },
  { sourceKey: 'cpu15MinLoad', nativeKey: 'cpu_15min_load', name: 'CPU 15 minute load', unit: 'load', transform: (valueArg) => SynologyDsmMapper.loadValue(valueArg) },
  { sourceKey: 'memoryRealUsage', nativeKey: 'memory_real_usage', name: 'Memory real usage', unit: '%', stateClass: 'measurement' },
  { sourceKey: 'memorySize', nativeKey: 'memory_size', name: 'Memory size', unit: 'B', deviceClass: 'data_size', stateClass: 'measurement', enabledByDefault: false },
  { sourceKey: 'memoryCached', nativeKey: 'memory_cached', name: 'Memory cached', unit: 'B', deviceClass: 'data_size', stateClass: 'measurement', enabledByDefault: false },
  { sourceKey: 'memoryAvailableSwap', nativeKey: 'memory_available_swap', name: 'Memory available swap', unit: 'B', deviceClass: 'data_size', stateClass: 'measurement' },
  { sourceKey: 'memoryAvailableReal', nativeKey: 'memory_available_real', name: 'Memory available real', unit: 'B', deviceClass: 'data_size', stateClass: 'measurement' },
  { sourceKey: 'memoryTotalSwap', nativeKey: 'memory_total_swap', name: 'Memory total swap', unit: 'B', deviceClass: 'data_size', stateClass: 'measurement' },
  { sourceKey: 'memoryTotalReal', nativeKey: 'memory_total_real', name: 'Memory total real', unit: 'B', deviceClass: 'data_size', stateClass: 'measurement' },
  { sourceKey: 'networkUp', nativeKey: 'network_up', name: 'Network up', unit: 'B/s', deviceClass: 'data_rate', stateClass: 'measurement' },
  { sourceKey: 'networkDown', nativeKey: 'network_down', name: 'Network down', unit: 'B/s', deviceClass: 'data_rate', stateClass: 'measurement' },
];

const volumeSensorDescriptors: TSynologyDsmSensorDescriptor<ISynologyDsmVolumeInfo>[] = [
  { sourceKey: 'status', nativeKey: 'volume_status', name: 'Status' },
  { sourceKey: 'sizeTotal', nativeKey: 'volume_size_total', name: 'Size total', unit: 'B', deviceClass: 'data_size', stateClass: 'measurement', enabledByDefault: false },
  { sourceKey: 'sizeUsed', nativeKey: 'volume_size_used', name: 'Size used', unit: 'B', deviceClass: 'data_size', stateClass: 'measurement' },
  { sourceKey: 'percentageUsed', nativeKey: 'volume_percentage_used', name: 'Percentage used', unit: '%', stateClass: 'measurement' },
  { sourceKey: 'diskTempAvg', nativeKey: 'volume_disk_temp_avg', name: 'Disk temperature average', unit: 'C', deviceClass: 'temperature', entityCategory: 'diagnostic' },
  { sourceKey: 'diskTempMax', nativeKey: 'volume_disk_temp_max', name: 'Disk temperature maximum', unit: 'C', deviceClass: 'temperature', entityCategory: 'diagnostic', enabledByDefault: false },
];

const diskSensorDescriptors: TSynologyDsmSensorDescriptor<ISynologyDsmDiskInfo>[] = [
  { sourceKey: 'smartStatus', nativeKey: 'disk_smart_status', name: 'S.M.A.R.T. status', entityCategory: 'diagnostic', enabledByDefault: false },
  { sourceKey: 'status', nativeKey: 'disk_status', name: 'Status', entityCategory: 'diagnostic' },
  { sourceKey: 'temperature', nativeKey: 'disk_temp', name: 'Temperature', unit: 'C', deviceClass: 'temperature', stateClass: 'measurement', entityCategory: 'diagnostic' },
];

export class SynologyDsmMapper {
  public static toSnapshot(configArg: ISynologyDsmConfig, connectedArg?: boolean, eventsArg: ISynologyDsmEvent[] = []): ISynologyDsmSnapshot {
    const source = configArg.snapshot;
    const sourceRecord = this.recordValue(source);
    const sourceStorage = this.recordValue(source?.storage);
    const configStorage = this.recordValue(configArg.storage);
    const sourceSurveillance = this.recordValue(sourceRecord?.surveillance);
    const configSurveillance = this.recordValue(configArg.surveillance);
    const system = this.systemInfo(configArg, source);
    const network = this.networkInfo(configArg, source, system);
    const utilization = this.utilizationInfo(configArg, source, network);
    const volumes = this.uniqueVolumes([
      ...this.arrayValues(sourceStorage?.volumes),
      ...this.arrayValues(sourceRecord?.volumes),
      ...this.arrayValues(configStorage?.volumes),
      ...this.arrayValues(configArg.volumes),
    ]);
    const disks = this.uniqueDisks([
      ...this.arrayValues(sourceStorage?.disks),
      ...this.arrayValues(sourceRecord?.disks),
      ...this.arrayValues(configStorage?.disks),
      ...this.arrayValues(configArg.disks),
    ]);
    const storage: ISynologyDsmStorageInfo = {
      ...(source?.storage || {}),
      ...(configArg.storage || {}),
      volumes,
      disks,
    };
    const cameras = this.uniqueCameras([
      ...this.arrayValues(source?.cameras),
      ...this.arrayValues(sourceSurveillance?.cameras),
      ...this.arrayValues(configArg.cameras),
      ...this.arrayValues(configSurveillance?.cameras),
    ]);
    const switches = this.uniqueSwitches([
      ...this.switchValues(source?.switches),
      ...this.switchValues(sourceRecord?.switches),
      ...this.switchValues(configArg.switches),
      ...this.homeModeSwitchValues(sourceSurveillance, configSurveillance),
    ]);
    const update = this.updateInfo([source?.update, sourceRecord?.update, configArg.update], system);
    const security = this.securityInfo(source?.security, configArg.security);
    const actions = this.uniqueActions([
      ...(source?.actions || []),
      ...(configArg.actions || []),
      ...this.actionsFromSystem(system),
      ...this.actionsFromSwitches(switches),
      ...this.actionsFromCameras(cameras),
    ]);
    const hasManualData = this.hasManualData(configArg, source, volumes, disks, cameras, switches, update, security);

    return {
      connected: connectedArg ?? configArg.online ?? configArg.connected ?? source?.connected ?? hasManualData,
      source: source?.source || (source ? 'snapshot' : hasManualData ? 'manual' : 'runtime'),
      updatedAt: source?.updatedAt || new Date().toISOString(),
      system,
      utilization,
      storage,
      network,
      cameras,
      update,
      switches,
      security,
      actions,
      events: [...(source?.events || []), ...(configArg.events || []), ...eventsArg],
      error: source?.error,
      metadata: this.cleanAttributes({
        ...source?.metadata,
        ...configArg.metadata,
        liveHttpImplemented: false,
        commandExecutorConfigured: Boolean(configArg.commandExecutor || configArg.nativeClient?.executeCommand),
      }),
    };
  }

  public static toDevices(snapshotArg: ISynologyDsmSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const updatedAt = snapshotArg.updatedAt || new Date().toISOString();
    const devices: plugins.shxInterfaces.data.IDeviceDefinition[] = [this.nasDevice(snapshotArg, updatedAt)];
    for (const volume of snapshotArg.storage.volumes) {
      devices.push(this.volumeDevice(snapshotArg, volume, updatedAt));
    }
    for (const disk of snapshotArg.storage.disks) {
      devices.push(this.diskDevice(snapshotArg, disk, updatedAt));
    }
    for (const camera of snapshotArg.cameras) {
      devices.push(this.cameraDevice(snapshotArg, camera, updatedAt));
    }
    return devices;
  }

  public static toEntities(snapshotArg: ISynologyDsmSnapshot): IIntegrationEntity[] {
    const entities: IIntegrationEntity[] = [];
    const usedIds = new Map<string, number>();
    const nasDeviceId = this.nasDeviceId(snapshotArg);
    const nasName = this.nasName(snapshotArg);
    const uniqueBase = this.uniqueBase(snapshotArg);

    entities.push(this.entity('binary_sensor', `${nasName} Integration connected`, nasDeviceId, `${uniqueBase}_integration_connected`, snapshotArg.connected ? 'on' : 'off', usedIds, {
      deviceClass: 'connectivity',
      host: snapshotArg.system.host,
      port: snapshotArg.system.port,
      ssl: snapshotArg.system.ssl,
    }, true));

    if (snapshotArg.system.temperature !== undefined) {
      entities.push(this.entity('sensor', `${nasName} Temperature`, nasDeviceId, `${uniqueBase}_temperature`, snapshotArg.system.temperature, usedIds, {
        nativeKey: 'temperature',
        unit: 'C',
        deviceClass: 'temperature',
        stateClass: 'measurement',
        entityCategory: 'diagnostic',
      }, snapshotArg.connected));
    }

    const uptime = this.uptimeValue(snapshotArg.system.uptimeSeconds);
    if (uptime !== undefined) {
      entities.push(this.entity('sensor', `${nasName} Uptime`, nasDeviceId, `${uniqueBase}_uptime`, uptime, usedIds, {
        nativeKey: 'uptime',
        deviceClass: 'timestamp',
        entityCategory: 'diagnostic',
        enabledByDefault: false,
      }, snapshotArg.connected));
    }

    for (const descriptor of utilizationSensorDescriptors) {
      const rawValue = snapshotArg.utilization[descriptor.sourceKey];
      const value = descriptor.transform ? descriptor.transform(rawValue) : rawValue;
      if (value === undefined || value === null) {
        continue;
      }
      entities.push(this.entity('sensor', `${nasName} ${descriptor.name}`, nasDeviceId, `${uniqueBase}_${descriptor.nativeKey}`, value, usedIds, {
        nativeKey: descriptor.nativeKey,
        unit: descriptor.unit,
        unitOfMeasurement: descriptor.unit,
        deviceClass: descriptor.deviceClass,
        stateClass: descriptor.stateClass,
        entityCategory: descriptor.entityCategory,
        enabledByDefault: descriptor.enabledByDefault,
      }, snapshotArg.connected));
    }

    if (snapshotArg.security?.status) {
      entities.push(this.entity('binary_sensor', `${nasName} Security status`, nasDeviceId, `${uniqueBase}_security_status`, snapshotArg.security.status === 'safe' ? 'off' : 'on', usedIds, {
        nativeKey: 'status',
        deviceClass: 'safety',
        entityCategory: 'diagnostic',
        status: snapshotArg.security.status,
        statusByCheck: snapshotArg.security.statusByCheck,
      }, snapshotArg.connected));
    }

    for (const volume of snapshotArg.storage.volumes) {
      this.pushVolumeEntities(entities, snapshotArg, volume, usedIds);
    }
    for (const disk of snapshotArg.storage.disks) {
      this.pushDiskEntities(entities, snapshotArg, disk, usedIds);
    }
    for (const camera of snapshotArg.cameras) {
      this.pushCameraEntities(entities, snapshotArg, camera, usedIds);
    }
    for (const switchState of snapshotArg.switches) {
      this.pushSwitchEntity(entities, snapshotArg, switchState, usedIds);
    }
    if (snapshotArg.update) {
      entities.push(this.updateEntity(snapshotArg, usedIds));
    }
    for (const action of this.snapshotActions(snapshotArg).filter((actionArg) => actionArg.target === 'system')) {
      entities.push(this.actionButton(snapshotArg, action, usedIds));
    }

    return entities;
  }

  public static commandForService(snapshotArg: ISynologyDsmSnapshot, requestArg: IServiceCallRequest): ISynologyDsmCommand | undefined {
    if (requestArg.domain === synologyDsmDomain && (requestArg.service === 'reboot' || requestArg.service === 'shutdown')) {
      const action = this.snapshotActions(snapshotArg).find((actionArg) => actionArg.target === 'system' && actionArg.action === requestArg.service);
      return action ? this.command(snapshotArg, requestArg, action) : undefined;
    }

    if (requestArg.domain === synologyDsmDomain && requestArg.service === 'set_home_mode' && typeof requestArg.data?.enabled === 'boolean') {
      const action = this.snapshotActions(snapshotArg).find((actionArg) => actionArg.target === 'switch' && actionArg.action === 'set_home_mode');
      return action ? this.command(snapshotArg, requestArg, action, undefined, { enabled: requestArg.data.enabled }) : undefined;
    }

    const targetEntity = this.findTargetEntity(snapshotArg, requestArg);
    if (requestArg.domain === 'button' && requestArg.service === 'press') {
      const nativeAction = this.stringValue(targetEntity?.attributes?.nativeAction) as TSynologyDsmCommandAction | undefined;
      const action = this.snapshotActions(snapshotArg).find((actionArg) => actionArg.target === 'system' && actionArg.action === nativeAction);
      return action ? this.command(snapshotArg, requestArg, action, targetEntity) : undefined;
    }

    if (requestArg.domain === 'switch' && (requestArg.service === 'turn_on' || requestArg.service === 'turn_off')) {
      const enabled = requestArg.service === 'turn_on';
      const switchKey = this.stringValue(targetEntity?.attributes?.switchKey);
      const action = this.snapshotActions(snapshotArg).find((actionArg) => actionArg.target === 'switch' && actionArg.action === 'set_home_mode' && (!switchKey || actionArg.switchKey === switchKey));
      return action ? this.command(snapshotArg, requestArg, action, targetEntity, { enabled }) : undefined;
    }

    if (requestArg.domain === 'camera' && (requestArg.service === 'enable_motion_detection' || requestArg.service === 'disable_motion_detection')) {
      const cameraId = this.stringValue(requestArg.data?.cameraId) || this.stringValue(targetEntity?.attributes?.cameraId) || this.cameraIdFromTarget(snapshotArg, requestArg);
      const expectedAction = requestArg.service === 'enable_motion_detection' ? 'enable_camera_motion_detection' : 'disable_camera_motion_detection';
      const action = this.snapshotActions(snapshotArg).find((actionArg) => actionArg.target === 'camera' && actionArg.action === expectedAction && (!cameraId || actionArg.cameraId === cameraId));
      return action ? this.command(snapshotArg, requestArg, action, targetEntity, { cameraId }) : undefined;
    }

    return undefined;
  }

  public static loadValue(valueArg: unknown): unknown {
    if (typeof valueArg === 'number' && Number.isInteger(valueArg)) {
      return Math.round((valueArg / 100) * 100) / 100;
    }
    return valueArg;
  }

  public static defaultPort(sslArg: boolean | undefined): number {
    return sslArg === false ? synologyDsmDefaultPort : synologyDsmDefaultSslPort;
  }

  public static normalizeMac(valueArg: string | undefined): string | undefined {
    if (!valueArg) {
      return undefined;
    }
    const compact = valueArg.toLowerCase().replace(/[^a-f0-9]/g, '');
    if (compact.length !== 12) {
      return valueArg.toLowerCase();
    }
    return compact.match(/.{1,2}/g)?.join(':');
  }

  private static nasDevice(snapshotArg: ISynologyDsmSnapshot, updatedAtArg: string): plugins.shxInterfaces.data.IDeviceDefinition {
    return {
      id: this.nasDeviceId(snapshotArg),
      integrationDomain: synologyDsmDomain,
      name: this.nasName(snapshotArg),
      protocol: snapshotArg.system.host ? 'http' : 'unknown',
      manufacturer,
      model: snapshotArg.system.model || 'Synology NAS',
      online: snapshotArg.connected,
      features: [
        { id: 'connection', capability: 'sensor', name: 'Connection', readable: true, writable: false },
        { id: 'temperature', capability: 'sensor', name: 'Temperature', readable: true, writable: false, unit: 'C' },
        { id: 'version', capability: 'sensor', name: 'DSM version', readable: true, writable: false },
        ...snapshotArg.switches.map((switchArg) => ({ id: switchArg.key, capability: 'switch' as const, name: switchArg.name || switchArg.key, readable: true, writable: true })),
      ],
      state: [
        { featureId: 'connection', value: snapshotArg.connected ? 'online' : 'offline', updatedAt: updatedAtArg },
        { featureId: 'temperature', value: snapshotArg.system.temperature ?? null, updatedAt: updatedAtArg },
        { featureId: 'version', value: snapshotArg.system.versionString || snapshotArg.system.version || null, updatedAt: updatedAtArg },
        ...snapshotArg.switches.map((switchArg) => ({ featureId: switchArg.key, value: switchArg.enabled, updatedAt: updatedAtArg })),
      ],
      metadata: this.cleanAttributes({
        serial: snapshotArg.system.serial,
        host: snapshotArg.system.host,
        port: snapshotArg.system.port,
        ssl: snapshotArg.system.ssl,
        macs: snapshotArg.system.macs,
        source: snapshotArg.source,
        error: snapshotArg.error,
      }),
    };
  }

  private static volumeDevice(snapshotArg: ISynologyDsmSnapshot, volumeArg: ISynologyDsmVolumeInfo, updatedAtArg: string): plugins.shxInterfaces.data.IDeviceDefinition {
    return {
      id: this.volumeDeviceId(snapshotArg, volumeArg),
      integrationDomain: synologyDsmDomain,
      name: `${this.nasName(snapshotArg)} (${this.volumeName(volumeArg)})`,
      protocol: 'unknown',
      manufacturer,
      model: volumeArg.deviceType || volumeArg.raidType || snapshotArg.system.model || 'DSM volume',
      online: snapshotArg.connected,
      features: [
        { id: 'status', capability: 'sensor', name: 'Status', readable: true, writable: false },
        { id: 'used', capability: 'sensor', name: 'Used', readable: true, writable: false, unit: 'B' },
        { id: 'used_percent', capability: 'sensor', name: 'Used percentage', readable: true, writable: false, unit: '%' },
      ],
      state: [
        { featureId: 'status', value: volumeArg.status || null, updatedAt: updatedAtArg },
        { featureId: 'used', value: volumeArg.sizeUsed ?? null, updatedAt: updatedAtArg },
        { featureId: 'used_percent', value: volumeArg.percentageUsed ?? null, updatedAt: updatedAtArg },
      ],
      metadata: this.cleanAttributes({ volumeId: volumeArg.id, viaDevice: this.nasDeviceId(snapshotArg) }),
    };
  }

  private static diskDevice(snapshotArg: ISynologyDsmSnapshot, diskArg: ISynologyDsmDiskInfo, updatedAtArg: string): plugins.shxInterfaces.data.IDeviceDefinition {
    return {
      id: this.diskDeviceId(snapshotArg, diskArg),
      integrationDomain: synologyDsmDomain,
      name: `${this.nasName(snapshotArg)} (${this.diskName(diskArg)})`,
      protocol: 'unknown',
      manufacturer: diskArg.vendor || manufacturer,
      model: diskArg.model || diskArg.diskType || 'DSM disk',
      online: snapshotArg.connected,
      features: [
        { id: 'status', capability: 'sensor', name: 'Status', readable: true, writable: false },
        { id: 'temperature', capability: 'sensor', name: 'Temperature', readable: true, writable: false, unit: 'C' },
      ],
      state: [
        { featureId: 'status', value: diskArg.status || null, updatedAt: updatedAtArg },
        { featureId: 'temperature', value: diskArg.temperature ?? null, updatedAt: updatedAtArg },
      ],
      metadata: this.cleanAttributes({ diskId: diskArg.id, firmware: diskArg.firmware, viaDevice: this.nasDeviceId(snapshotArg) }),
    };
  }

  private static cameraDevice(snapshotArg: ISynologyDsmSnapshot, cameraArg: ISynologyDsmCameraInfo, updatedAtArg: string): plugins.shxInterfaces.data.IDeviceDefinition {
    return {
      id: this.cameraDeviceId(snapshotArg, cameraArg),
      integrationDomain: synologyDsmDomain,
      name: cameraArg.name || `Camera ${cameraArg.id}`,
      protocol: 'unknown',
      manufacturer,
      model: cameraArg.model || 'Surveillance Station camera',
      online: snapshotArg.connected && cameraArg.enabled !== false,
      features: [
        { id: 'enabled', capability: 'sensor', name: 'Enabled', readable: true, writable: false },
        { id: 'recording', capability: 'sensor', name: 'Recording', readable: true, writable: false },
        { id: 'motion_detection', capability: 'sensor', name: 'Motion detection', readable: true, writable: true },
      ],
      state: [
        { featureId: 'enabled', value: cameraArg.enabled ?? null, updatedAt: updatedAtArg },
        { featureId: 'recording', value: cameraArg.recording ?? null, updatedAt: updatedAtArg },
        { featureId: 'motion_detection', value: cameraArg.motionDetectionEnabled ?? null, updatedAt: updatedAtArg },
      ],
      metadata: this.cleanAttributes({ cameraId: cameraArg.id, rtsp: cameraArg.rtsp, nativePlatform: 'camera', viaDevice: this.nasDeviceId(snapshotArg) }),
    };
  }

  private static pushVolumeEntities(entitiesArg: IIntegrationEntity[], snapshotArg: ISynologyDsmSnapshot, volumeArg: ISynologyDsmVolumeInfo, usedIdsArg: Map<string, number>): void {
    for (const descriptor of volumeSensorDescriptors) {
      const value = volumeArg[descriptor.sourceKey];
      if (value === undefined || value === null) {
        continue;
      }
      entitiesArg.push(this.entity('sensor', `${this.volumeName(volumeArg)} ${descriptor.name}`, this.volumeDeviceId(snapshotArg, volumeArg), `${this.uniqueBase(snapshotArg)}_${descriptor.nativeKey}_${this.slug(volumeArg.id)}`, value, usedIdsArg, {
        nativeKey: descriptor.nativeKey,
        volumeId: volumeArg.id,
        unit: descriptor.unit,
        unitOfMeasurement: descriptor.unit,
        deviceClass: descriptor.deviceClass,
        stateClass: descriptor.stateClass,
        entityCategory: descriptor.entityCategory,
        enabledByDefault: descriptor.enabledByDefault,
      }, snapshotArg.connected));
    }
  }

  private static pushDiskEntities(entitiesArg: IIntegrationEntity[], snapshotArg: ISynologyDsmSnapshot, diskArg: ISynologyDsmDiskInfo, usedIdsArg: Map<string, number>): void {
    for (const descriptor of diskSensorDescriptors) {
      const value = diskArg[descriptor.sourceKey];
      if (value === undefined || value === null) {
        continue;
      }
      entitiesArg.push(this.entity('sensor', `${this.diskName(diskArg)} ${descriptor.name}`, this.diskDeviceId(snapshotArg, diskArg), `${this.uniqueBase(snapshotArg)}_${descriptor.nativeKey}_${this.slug(diskArg.id)}`, value, usedIdsArg, {
        nativeKey: descriptor.nativeKey,
        diskId: diskArg.id,
        unit: descriptor.unit,
        unitOfMeasurement: descriptor.unit,
        deviceClass: descriptor.deviceClass,
        stateClass: descriptor.stateClass,
        entityCategory: descriptor.entityCategory,
        enabledByDefault: descriptor.enabledByDefault,
      }, snapshotArg.connected));
    }
    if (diskArg.exceedBadSectorThreshold !== undefined) {
      entitiesArg.push(this.entity('binary_sensor', `${this.diskName(diskArg)} Bad sector threshold`, this.diskDeviceId(snapshotArg, diskArg), `${this.uniqueBase(snapshotArg)}_disk_exceed_bad_sector_thr_${this.slug(diskArg.id)}`, diskArg.exceedBadSectorThreshold ? 'on' : 'off', usedIdsArg, {
        nativeKey: 'disk_exceed_bad_sector_thr',
        diskId: diskArg.id,
        deviceClass: 'safety',
        entityCategory: 'diagnostic',
      }, snapshotArg.connected));
    }
    if (diskArg.belowRemainLifeThreshold !== undefined) {
      entitiesArg.push(this.entity('binary_sensor', `${this.diskName(diskArg)} Remaining life threshold`, this.diskDeviceId(snapshotArg, diskArg), `${this.uniqueBase(snapshotArg)}_disk_below_remain_life_thr_${this.slug(diskArg.id)}`, diskArg.belowRemainLifeThreshold ? 'on' : 'off', usedIdsArg, {
        nativeKey: 'disk_below_remain_life_thr',
        diskId: diskArg.id,
        deviceClass: 'safety',
        entityCategory: 'diagnostic',
      }, snapshotArg.connected));
    }
  }

  private static pushCameraEntities(entitiesArg: IIntegrationEntity[], snapshotArg: ISynologyDsmSnapshot, cameraArg: ISynologyDsmCameraInfo, usedIdsArg: Map<string, number>): void {
    const deviceId = this.cameraDeviceId(snapshotArg, cameraArg);
    const name = cameraArg.name || `Camera ${cameraArg.id}`;
    if (cameraArg.enabled !== undefined) {
      entitiesArg.push(this.entity('binary_sensor', `${name} Enabled`, deviceId, `${this.uniqueBase(snapshotArg)}_camera_enabled_${this.slug(cameraArg.id)}`, cameraArg.enabled ? 'on' : 'off', usedIdsArg, {
        nativePlatform: 'camera',
        nativeKey: 'is_enabled',
        cameraId: cameraArg.id,
      }, snapshotArg.connected));
    }
    if (cameraArg.recording !== undefined) {
      entitiesArg.push(this.entity('binary_sensor', `${name} Recording`, deviceId, `${this.uniqueBase(snapshotArg)}_camera_recording_${this.slug(cameraArg.id)}`, cameraArg.recording ? 'on' : 'off', usedIdsArg, {
        nativePlatform: 'camera',
        nativeKey: 'is_recording',
        cameraId: cameraArg.id,
      }, snapshotArg.connected && cameraArg.enabled !== false));
    }
    if (cameraArg.motionDetectionEnabled !== undefined) {
      entitiesArg.push(this.entity('binary_sensor', `${name} Motion detection`, deviceId, `${this.uniqueBase(snapshotArg)}_camera_motion_detection_${this.slug(cameraArg.id)}`, cameraArg.motionDetectionEnabled ? 'on' : 'off', usedIdsArg, {
        nativePlatform: 'camera',
        nativeKey: 'is_motion_detection_enabled',
        nativeAction: cameraArg.motionDetectionEnabled ? 'disable_camera_motion_detection' : 'enable_camera_motion_detection',
        cameraId: cameraArg.id,
        writable: true,
      }, snapshotArg.connected && cameraArg.enabled !== false));
    }
    if (cameraArg.rtsp) {
      entitiesArg.push(this.entity('sensor', `${name} Stream source`, deviceId, `${this.uniqueBase(snapshotArg)}_camera_stream_source_${this.slug(cameraArg.id)}`, cameraArg.rtsp, usedIdsArg, {
        nativePlatform: 'camera',
        nativeKey: 'stream_source',
        cameraId: cameraArg.id,
        rtsp: cameraArg.rtsp,
      }, snapshotArg.connected && cameraArg.enabled !== false));
    }
  }

  private static pushSwitchEntity(entitiesArg: IIntegrationEntity[], snapshotArg: ISynologyDsmSnapshot, switchArg: ISynologyDsmSwitchInfo, usedIdsArg: Map<string, number>): void {
    if (switchArg.type !== 'home_mode' && switchArg.key !== 'home_mode') {
      return;
    }
    entitiesArg.push(this.entity('switch', `${this.nasName(snapshotArg)} Surveillance Station Home mode`, this.nasDeviceId(snapshotArg), `${this.uniqueBase(snapshotArg)}_surveillance_station_home_mode`, switchArg.enabled ? 'on' : 'off', usedIdsArg, {
      nativeKey: 'home_mode',
      nativeType: 'home_mode',
      nativeAction: 'set_home_mode',
      switchKey: switchArg.key,
      entityCategory: 'config',
      writable: true,
    }, snapshotArg.connected));
  }

  private static updateEntity(snapshotArg: ISynologyDsmSnapshot, usedIdsArg: Map<string, number>): IIntegrationEntity {
    const update = snapshotArg.update || {};
    const installedVersion = update.installedVersion || snapshotArg.system.versionString || snapshotArg.system.version;
    const latestVersion = update.updateAvailable ? update.latestVersion || installedVersion : installedVersion;
    return this.entity('update', `${this.nasName(snapshotArg)} DSM update`, this.nasDeviceId(snapshotArg), `${this.uniqueBase(snapshotArg)}_dsm_update`, update.updateAvailable ? 'on' : 'off', usedIdsArg, {
      nativeKey: 'update',
      installedVersion,
      latestVersion,
      releaseUrl: update.releaseUrl,
      entityCategory: 'diagnostic',
      writable: false,
    }, snapshotArg.connected);
  }

  private static actionButton(snapshotArg: ISynologyDsmSnapshot, actionArg: ISynologyDsmActionDescriptor, usedIdsArg: Map<string, number>): IIntegrationEntity {
    return this.entity('button', `${this.nasName(snapshotArg)} ${this.title(actionArg.action)}`, this.nasDeviceId(snapshotArg), `${this.uniqueBase(snapshotArg)}_${this.slug(actionArg.action)}`, 'available', usedIdsArg, {
      nativeType: 'system_action',
      nativeAction: actionArg.action,
      actionTarget: 'system',
      entityCategory: 'config',
      writable: true,
    }, snapshotArg.connected, actionArg.entityId);
  }

  private static command(snapshotArg: ISynologyDsmSnapshot, requestArg: IServiceCallRequest, actionArg: ISynologyDsmActionDescriptor, entityArg?: IIntegrationEntity, payloadArg: Record<string, unknown> = {}): ISynologyDsmCommand {
    return {
      type: actionArg.target === 'system' ? 'system.action' : actionArg.target === 'camera' ? 'camera.action' : 'switch.set',
      service: requestArg.service,
      action: actionArg.action,
      target: requestArg.target,
      serial: snapshotArg.system.serial,
      deviceId: entityArg?.deviceId || actionArg.deviceId || requestArg.target.deviceId,
      entityId: entityArg?.id || actionArg.entityId || requestArg.target.entityId,
      cameraId: actionArg.cameraId || this.stringValue(entityArg?.attributes?.cameraId) || this.stringValue(payloadArg.cameraId),
      switchKey: actionArg.switchKey || this.stringValue(entityArg?.attributes?.switchKey),
      payload: this.cleanAttributes(payloadArg),
      snapshotSource: snapshotArg.source,
    };
  }

  private static findTargetEntity(snapshotArg: ISynologyDsmSnapshot, requestArg: IServiceCallRequest): IIntegrationEntity | undefined {
    const entities = this.toEntities(snapshotArg);
    if (requestArg.target.entityId) {
      return entities.find((entityArg) => entityArg.id === requestArg.target.entityId || entityArg.uniqueId === requestArg.target.entityId);
    }
    if (requestArg.target.deviceId) {
      return entities.find((entityArg) => entityArg.deviceId === requestArg.target.deviceId);
    }
    return undefined;
  }

  private static cameraIdFromTarget(snapshotArg: ISynologyDsmSnapshot, requestArg: IServiceCallRequest): string | undefined {
    if (!requestArg.target.deviceId) {
      return undefined;
    }
    return snapshotArg.cameras.find((cameraArg) => this.cameraDeviceId(snapshotArg, cameraArg) === requestArg.target.deviceId)?.id;
  }

  private static snapshotActions(snapshotArg: ISynologyDsmSnapshot): ISynologyDsmActionDescriptor[] {
    return snapshotArg.actions || [];
  }

  private static actionsFromSystem(systemArg: ISynologyDsmSystemInfo): ISynologyDsmActionDescriptor[] {
    if (!systemArg.serial && !systemArg.host && !systemArg.name) {
      return [];
    }
    return [
      { target: 'system', action: 'reboot' },
      { target: 'system', action: 'shutdown' },
    ];
  }

  private static actionsFromSwitches(switchesArg: ISynologyDsmSwitchInfo[]): ISynologyDsmActionDescriptor[] {
    return switchesArg
      .filter((switchArg) => switchArg.type === 'home_mode' || switchArg.key === 'home_mode')
      .map((switchArg) => ({ target: 'switch' as const, action: 'set_home_mode' as const, switchKey: switchArg.key }));
  }

  private static actionsFromCameras(camerasArg: ISynologyDsmCameraInfo[]): ISynologyDsmActionDescriptor[] {
    return camerasArg.flatMap((cameraArg) => [
      { target: 'camera' as const, action: 'enable_camera_motion_detection' as const, cameraId: cameraArg.id },
      { target: 'camera' as const, action: 'disable_camera_motion_detection' as const, cameraId: cameraArg.id },
    ]);
  }

  private static systemInfo(configArg: ISynologyDsmConfig, sourceArg?: ISynologyDsmSnapshot): ISynologyDsmSystemInfo {
    const records = [sourceArg?.system, configArg.system, configArg.information].map((recordArg) => this.recordValue(recordArg)).filter((recordArg): recordArg is Record<string, unknown> => Boolean(recordArg));
    const ssl = configArg.ssl ?? this.firstBoolean(records, 'ssl', 'use_ssl') ?? synologyDsmDefaultSsl;
    const port = configArg.port || this.firstNumber(records, 'port') || this.defaultPort(ssl);
    const macs = this.uniqueStrings([
      ...records.flatMap((recordArg) => this.stringArray(recordArg.macs)),
      ...records.flatMap((recordArg) => this.stringArray(recordArg.mac)),
      ...this.stringArray(configArg.macs),
      ...this.stringArray(configArg.macAddress),
    ].map((valueArg) => this.normalizeMac(valueArg) || valueArg));
    const version = this.firstString(records, 'versionString', 'version_string', 'version', 'firmware');
    return this.cleanAttributes({
      id: sourceArg?.system.id || configArg.uniqueId || configArg.serial || macs[0] || (configArg.host ? `${configArg.host}:${port}` : undefined),
      serial: this.firstString(records, 'serial', 'serialNumber', 'serial_number') || configArg.serial,
      name: configArg.name || this.firstString(records, 'name', 'friendlyName', 'friendly_name', 'hostname') || configArg.host,
      hostname: this.firstString(records, 'hostname', 'hostName', 'host_name') || configArg.network?.hostname || configArg.host,
      host: configArg.host || this.firstString(records, 'host'),
      port,
      ssl,
      verifySsl: configArg.verifySsl ?? this.firstBoolean(records, 'verifySsl', 'verify_ssl'),
      manufacturer,
      model: this.firstString(records, 'model', 'modelName', 'model_name'),
      version,
      versionString: version,
      temperature: this.firstNumber(records, 'temperature', 'system_temp'),
      uptimeSeconds: this.firstNumber(records, 'uptimeSeconds', 'uptime_seconds', 'uptime'),
      macs,
    }) as ISynologyDsmSystemInfo;
  }

  private static networkInfo(configArg: ISynologyDsmConfig, sourceArg: ISynologyDsmSnapshot | undefined, systemArg: ISynologyDsmSystemInfo): ISynologyDsmNetworkInfo {
    const records = [sourceArg?.network, configArg.network].map((recordArg) => this.recordValue(recordArg)).filter((recordArg): recordArg is Record<string, unknown> => Boolean(recordArg));
    const macs = this.uniqueStrings([
      ...(systemArg.macs || []),
      ...records.flatMap((recordArg) => this.stringArray(recordArg.macs)),
      ...records.flatMap((recordArg) => this.stringArray(recordArg.mac)),
    ].map((valueArg) => this.normalizeMac(valueArg) || valueArg));
    return this.cleanAttributes({
      ...(sourceArg?.network || {}),
      ...(configArg.network || {}),
      hostname: this.firstString(records, 'hostname', 'hostName', 'host_name') || systemArg.hostname,
      macs,
      uploadRate: this.firstNumber(records, 'uploadRate', 'networkUp', 'network_up', 'up'),
      downloadRate: this.firstNumber(records, 'downloadRate', 'networkDown', 'network_down', 'down'),
      interfaces: this.arrayValues(records.flatMap((recordArg) => this.arrayValues(recordArg.interfaces))).map((interfaceArg) => this.normalizeNetworkInterface(interfaceArg)).filter((interfaceArg): interfaceArg is NonNullable<ReturnType<typeof this.normalizeNetworkInterface>> => Boolean(interfaceArg)),
    }) as ISynologyDsmNetworkInfo;
  }

  private static utilizationInfo(configArg: ISynologyDsmConfig, sourceArg: ISynologyDsmSnapshot | undefined, networkArg: ISynologyDsmNetworkInfo): ISynologyDsmUtilizationInfo {
    const records = [sourceArg?.utilization, configArg.utilization].map((recordArg) => this.recordValue(recordArg)).filter((recordArg): recordArg is Record<string, unknown> => Boolean(recordArg));
    return this.cleanAttributes({
      ...(sourceArg?.utilization || {}),
      ...(configArg.utilization || {}),
      cpuOtherLoad: this.firstNumber(records, 'cpuOtherLoad', 'cpu_other_load'),
      cpuUserLoad: this.firstNumber(records, 'cpuUserLoad', 'cpu_user_load'),
      cpuSystemLoad: this.firstNumber(records, 'cpuSystemLoad', 'cpu_system_load'),
      cpuTotalLoad: this.firstNumber(records, 'cpuTotalLoad', 'cpu_total_load'),
      cpu1MinLoad: this.firstNumber(records, 'cpu1MinLoad', 'cpu_1min_load'),
      cpu5MinLoad: this.firstNumber(records, 'cpu5MinLoad', 'cpu_5min_load'),
      cpu15MinLoad: this.firstNumber(records, 'cpu15MinLoad', 'cpu_15min_load'),
      memoryRealUsage: this.firstNumber(records, 'memoryRealUsage', 'memory_real_usage'),
      memorySize: this.firstNumber(records, 'memorySize', 'memory_size'),
      memoryCached: this.firstNumber(records, 'memoryCached', 'memory_cached'),
      memoryAvailableSwap: this.firstNumber(records, 'memoryAvailableSwap', 'memory_available_swap'),
      memoryAvailableReal: this.firstNumber(records, 'memoryAvailableReal', 'memory_available_real'),
      memoryTotalSwap: this.firstNumber(records, 'memoryTotalSwap', 'memory_total_swap'),
      memoryTotalReal: this.firstNumber(records, 'memoryTotalReal', 'memory_total_real'),
      networkUp: this.firstNumber(records, 'networkUp', 'network_up') ?? networkArg.uploadRate,
      networkDown: this.firstNumber(records, 'networkDown', 'network_down') ?? networkArg.downloadRate,
    }) as ISynologyDsmUtilizationInfo;
  }

  private static normalizeVolume(valueArg: unknown): ISynologyDsmVolumeInfo | undefined {
    if (typeof valueArg === 'string' && valueArg.trim()) {
      return { id: valueArg.trim() };
    }
    const record = this.recordValue(valueArg);
    if (!record) {
      return undefined;
    }
    const id = this.stringValue(record.id) || this.stringValue(record.volumeId) || this.stringValue(record.volume_id) || this.stringValue(record.name);
    if (!id) {
      return undefined;
    }
    return this.cleanAttributes({
      ...record,
      id,
      name: this.stringValue(record.name) || id.replace(/_/g, ' '),
      status: this.stringValue(record.status) || this.stringValue(record.volume_status),
      sizeTotal: this.numberValue(record.sizeTotal) ?? this.numberValue(record.size_total) ?? this.numberValue(record.volume_size_total) ?? this.numberValue(record.total),
      sizeUsed: this.numberValue(record.sizeUsed) ?? this.numberValue(record.size_used) ?? this.numberValue(record.volume_size_used) ?? this.numberValue(record.used),
      percentageUsed: this.numberValue(record.percentageUsed) ?? this.numberValue(record.percentage_used) ?? this.numberValue(record.volume_percentage_used) ?? this.numberValue(record.percent),
      diskTempAvg: this.numberValue(record.diskTempAvg) ?? this.numberValue(record.disk_temp_avg) ?? this.numberValue(record.volume_disk_temp_avg),
      diskTempMax: this.numberValue(record.diskTempMax) ?? this.numberValue(record.disk_temp_max) ?? this.numberValue(record.volume_disk_temp_max),
      deviceType: this.stringValue(record.deviceType) || this.stringValue(record.device_type),
      raidType: this.stringValue(record.raidType) || this.stringValue(record.raid_type),
    }) as ISynologyDsmVolumeInfo;
  }

  private static normalizeDisk(valueArg: unknown): ISynologyDsmDiskInfo | undefined {
    if (typeof valueArg === 'string' && valueArg.trim()) {
      return { id: valueArg.trim(), name: valueArg.trim() };
    }
    const record = this.recordValue(valueArg);
    if (!record) {
      return undefined;
    }
    const id = this.stringValue(record.id) || this.stringValue(record.diskId) || this.stringValue(record.disk_id) || this.stringValue(record.name);
    if (!id) {
      return undefined;
    }
    return this.cleanAttributes({
      ...record,
      id,
      name: this.stringValue(record.name) || id,
      vendor: this.stringValue(record.vendor) || this.stringValue(record.manufacturer),
      model: this.stringValue(record.model)?.trim(),
      firmware: this.stringValue(record.firmware) || this.stringValue(record.firm),
      diskType: this.stringValue(record.diskType) || this.stringValue(record.disk_type),
      status: this.stringValue(record.status) || this.stringValue(record.disk_status),
      smartStatus: this.stringValue(record.smartStatus) || this.stringValue(record.smart_status) || this.stringValue(record.disk_smart_status),
      temperature: this.numberValue(record.temperature) ?? this.numberValue(record.temp) ?? this.numberValue(record.disk_temp),
      exceedBadSectorThreshold: this.booleanValue(record.exceedBadSectorThreshold) ?? this.booleanValue(record.exceed_bad_sector_thr) ?? this.booleanValue(record.disk_exceed_bad_sector_thr),
      belowRemainLifeThreshold: this.booleanValue(record.belowRemainLifeThreshold) ?? this.booleanValue(record.below_remain_life_thr) ?? this.booleanValue(record.disk_below_remain_life_thr),
    }) as ISynologyDsmDiskInfo;
  }

  private static normalizeCamera(valueArg: unknown): ISynologyDsmCameraInfo | undefined {
    const record = this.recordValue(valueArg);
    if (!record) {
      return undefined;
    }
    const id = this.stringValue(record.id) || this.stringValue(record.cameraId) || this.stringValue(record.camera_id);
    if (!id) {
      return undefined;
    }
    const liveView = this.recordValue(record.liveView) || this.recordValue(record.live_view);
    return this.cleanAttributes({
      ...record,
      id,
      name: this.stringValue(record.name) || `Camera ${id}`,
      model: this.stringValue(record.model),
      enabled: this.booleanValue(record.enabled) ?? this.booleanValue(record.is_enabled),
      recording: this.booleanValue(record.recording) ?? this.booleanValue(record.is_recording),
      motionDetectionEnabled: this.booleanValue(record.motionDetectionEnabled) ?? this.booleanValue(record.motion_detection_enabled) ?? this.booleanValue(record.is_motion_detection_enabled),
      rtsp: this.stringValue(record.rtsp) || this.stringValue(liveView?.rtsp),
      snapshotUrl: this.stringValue(record.snapshotUrl) || this.stringValue(record.snapshot_url),
    }) as ISynologyDsmCameraInfo;
  }

  private static normalizeSwitch(valueArg: unknown): ISynologyDsmSwitchInfo | undefined {
    const record = this.recordValue(valueArg);
    if (!record) {
      return undefined;
    }
    const key = this.stringValue(record.key) || this.stringValue(record.id) || this.stringValue(record.name);
    const enabled = this.booleanValue(record.enabled) ?? this.booleanValue(record.is_on) ?? this.booleanValue(record.value) ?? this.booleanValue(record.state);
    if (!key || enabled === undefined) {
      return undefined;
    }
    return this.cleanAttributes({
      ...record,
      key,
      name: this.stringValue(record.name) || this.title(key),
      enabled,
      type: this.stringValue(record.type) || (key === 'home_mode' || key === 'homeMode' ? 'home_mode' : key),
    }) as ISynologyDsmSwitchInfo;
  }

  private static normalizeNetworkInterface(valueArg: unknown) {
    const record = this.recordValue(valueArg);
    if (!record) {
      return undefined;
    }
    const name = this.stringValue(record.name) || this.stringValue(record.id);
    if (!name) {
      return undefined;
    }
    return this.cleanAttributes({
      ...record,
      id: this.stringValue(record.id) || name,
      name,
      mac: this.normalizeMac(this.stringValue(record.mac) || this.stringValue(record.macAddress) || this.stringValue(record.mac_address)),
      ipAddress: this.stringValue(record.ipAddress) || this.stringValue(record.ip_address) || this.stringValue(record.ip),
      ipv6Address: this.stringValue(record.ipv6Address) || this.stringValue(record.ipv6_address) || this.stringValue(record.ipv6),
      connected: this.booleanValue(record.connected) ?? this.booleanValue(record.is_up),
      speedMbps: this.numberValue(record.speedMbps) ?? this.numberValue(record.speed_mbps) ?? this.numberValue(record.speed),
    });
  }

  private static updateInfo(recordsArg: unknown[], systemArg: ISynologyDsmSystemInfo): ISynologyDsmUpdateInfo | undefined {
    const records = recordsArg.map((recordArg) => this.recordValue(recordArg)).filter((recordArg): recordArg is Record<string, unknown> => Boolean(recordArg));
    if (!records.length) {
      return undefined;
    }
    const updateAvailable = this.firstBoolean(records, 'updateAvailable', 'update_available');
    const installedVersion = this.firstString(records, 'installedVersion', 'installed_version', 'currentVersion', 'current_version') || systemArg.versionString || systemArg.version;
    const latestVersion = this.firstString(records, 'latestVersion', 'latest_version', 'availableVersion', 'available_version') || (updateAvailable ? undefined : installedVersion);
    const details = records.map((recordArg) => this.recordValue(recordArg.availableVersionDetails) || this.recordValue(recordArg.available_version_details)).find(Boolean);
    return this.cleanAttributes({
      ...records.reduce((accumulatorArg, recordArg) => ({ ...accumulatorArg, ...recordArg }), {}),
      installedVersion,
      latestVersion,
      updateAvailable,
      releaseUrl: this.firstString(records, 'releaseUrl', 'release_url') || this.releaseUrlFromDetails(details, systemArg.model),
      availableVersionDetails: details,
    }) as ISynologyDsmUpdateInfo;
  }

  private static securityInfo(...valuesArg: unknown[]) {
    const records = valuesArg.map((recordArg) => this.recordValue(recordArg)).filter((recordArg): recordArg is Record<string, unknown> => Boolean(recordArg));
    if (!records.length) {
      return undefined;
    }
    const merged = records.reduce((accumulatorArg, recordArg) => ({ ...accumulatorArg, ...recordArg }), {} as Record<string, unknown>);
    return this.cleanAttributes({
      ...merged,
      status: this.firstString(records, 'status'),
      statusByCheck: this.stringRecordValue(merged.statusByCheck) || this.stringRecordValue(merged.status_by_check),
    });
  }

  private static uniqueVolumes(valuesArg: unknown[]): ISynologyDsmVolumeInfo[] {
    return this.uniqueBy(valuesArg.map((valueArg) => this.normalizeVolume(valueArg)).filter((valueArg): valueArg is ISynologyDsmVolumeInfo => Boolean(valueArg)), (valueArg) => valueArg.id);
  }

  private static uniqueDisks(valuesArg: unknown[]): ISynologyDsmDiskInfo[] {
    return this.uniqueBy(valuesArg.map((valueArg) => this.normalizeDisk(valueArg)).filter((valueArg): valueArg is ISynologyDsmDiskInfo => Boolean(valueArg)), (valueArg) => valueArg.id);
  }

  private static uniqueCameras(valuesArg: unknown[]): ISynologyDsmCameraInfo[] {
    return this.uniqueBy(valuesArg.map((valueArg) => this.normalizeCamera(valueArg)).filter((valueArg): valueArg is ISynologyDsmCameraInfo => Boolean(valueArg)), (valueArg) => valueArg.id);
  }

  private static uniqueSwitches(valuesArg: unknown[]): ISynologyDsmSwitchInfo[] {
    return this.uniqueBy(valuesArg.map((valueArg) => this.normalizeSwitch(valueArg)).filter((valueArg): valueArg is ISynologyDsmSwitchInfo => Boolean(valueArg)), (valueArg) => valueArg.key);
  }

  private static uniqueActions(valuesArg: ISynologyDsmActionDescriptor[]): ISynologyDsmActionDescriptor[] {
    return this.uniqueBy(valuesArg, (valueArg) => [valueArg.target, valueArg.action, valueArg.cameraId || '', valueArg.switchKey || ''].join(':'));
  }

  private static uniqueBy<TValue>(valuesArg: TValue[], keyFnArg: (valueArg: TValue) => string): TValue[] {
    const values = new Map<string, TValue>();
    for (const value of valuesArg) {
      values.set(keyFnArg(value), value);
    }
    return [...values.values()];
  }

  private static switchValues(valueArg: unknown): unknown[] {
    if (Array.isArray(valueArg)) {
      return valueArg;
    }
    const record = this.recordValue(valueArg);
    if (!record) {
      return [];
    }
    return Object.entries(record).map(([key, value]) => this.recordValue(value) ? { key, ...this.recordValue(value) } : { key, value });
  }

  private static homeModeSwitchValues(...recordsArg: Array<Record<string, unknown> | undefined>): unknown[] {
    return recordsArg.flatMap((recordArg) => {
      if (!recordArg) {
        return [];
      }
      const value = this.booleanValue(recordArg.homeMode) ?? this.booleanValue(recordArg.home_mode);
      return value === undefined ? [] : [{ key: 'home_mode', name: 'Home mode', type: 'home_mode', enabled: value }];
    });
  }

  private static hasManualData(configArg: ISynologyDsmConfig, sourceArg: ISynologyDsmSnapshot | undefined, volumesArg: ISynologyDsmVolumeInfo[], disksArg: ISynologyDsmDiskInfo[], camerasArg: ISynologyDsmCameraInfo[], switchesArg: ISynologyDsmSwitchInfo[], updateArg?: ISynologyDsmUpdateInfo, securityArg?: unknown): boolean {
    return Boolean(
      sourceArg
      || configArg.system
      || configArg.information
      || configArg.utilization
      || configArg.storage
      || configArg.network
      || volumesArg.length
      || disksArg.length
      || camerasArg.length
      || switchesArg.length
      || updateArg
      || securityArg
    );
  }

  private static entity(platformArg: TEntityPlatform, nameArg: string, deviceIdArg: string, uniqueIdArg: string, stateArg: unknown, usedIdsArg: Map<string, number>, attributesArg: Record<string, unknown> = {}, availableArg = true, explicitIdArg?: string): IIntegrationEntity {
    return {
      id: explicitIdArg || this.entityId(platformArg, nameArg, usedIdsArg),
      uniqueId: `${synologyDsmDomain}_${uniqueIdArg}`,
      integrationDomain: synologyDsmDomain,
      deviceId: deviceIdArg,
      platform: platformArg,
      name: nameArg,
      state: stateArg,
      attributes: this.cleanAttributes(attributesArg),
      available: availableArg,
    };
  }

  private static entityId(platformArg: TEntityPlatform, nameArg: string, usedIdsArg: Map<string, number>): string {
    const base = `${platformArg}.${this.slug(nameArg)}`;
    const count = usedIdsArg.get(base) || 0;
    usedIdsArg.set(base, count + 1);
    return count ? `${base}_${count + 1}` : base;
  }

  private static nasDeviceId(snapshotArg: ISynologyDsmSnapshot): string {
    return `synology_dsm.nas.${this.slug(this.uniqueBase(snapshotArg))}`;
  }

  private static volumeDeviceId(snapshotArg: ISynologyDsmSnapshot, volumeArg: ISynologyDsmVolumeInfo): string {
    return `synology_dsm.volume.${this.slug(this.uniqueBase(snapshotArg))}.${this.slug(volumeArg.id)}`;
  }

  private static diskDeviceId(snapshotArg: ISynologyDsmSnapshot, diskArg: ISynologyDsmDiskInfo): string {
    return `synology_dsm.disk.${this.slug(this.uniqueBase(snapshotArg))}.${this.slug(diskArg.id)}`;
  }

  private static cameraDeviceId(snapshotArg: ISynologyDsmSnapshot, cameraArg: ISynologyDsmCameraInfo): string {
    return `synology_dsm.camera.${this.slug(this.uniqueBase(snapshotArg))}.${this.slug(cameraArg.id)}`;
  }

  private static uniqueBase(snapshotArg: ISynologyDsmSnapshot): string {
    return snapshotArg.system.serial || snapshotArg.system.id || snapshotArg.system.macs?.[0] || snapshotArg.system.host || snapshotArg.system.name || 'synology_dsm';
  }

  private static nasName(snapshotArg: ISynologyDsmSnapshot): string {
    return snapshotArg.system.name || snapshotArg.network.hostname || snapshotArg.system.hostname || snapshotArg.system.model || snapshotArg.system.host || 'Synology DSM';
  }

  private static volumeName(volumeArg: ISynologyDsmVolumeInfo): string {
    return volumeArg.name || volumeArg.id.replace(/_/g, ' ').replace(/^./, (valueArg) => valueArg.toUpperCase());
  }

  private static diskName(diskArg: ISynologyDsmDiskInfo): string {
    return diskArg.name || diskArg.id;
  }

  private static uptimeValue(valueArg: unknown): string | undefined {
    if (typeof valueArg === 'string' && valueArg) {
      return valueArg;
    }
    if (typeof valueArg === 'number' && Number.isFinite(valueArg)) {
      return new Date(Date.now() - valueArg * 1000).toISOString();
    }
    return undefined;
  }

  private static releaseUrlFromDetails(detailsArg: Record<string, unknown> | undefined, modelArg: string | undefined): string | undefined {
    if (!detailsArg || !modelArg) {
      return undefined;
    }
    const buildNumber = this.stringValue(detailsArg.buildnumber) || this.stringValue(detailsArg.buildNumber) || this.stringValue(detailsArg.build_number);
    const nano = this.numberValue(detailsArg.nano) || 0;
    if (!buildNumber) {
      return undefined;
    }
    const updateVersion = nano > 0 ? `${buildNumber}-${nano}` : buildNumber;
    const query = new URLSearchParams({ model: modelArg, update_version: updateVersion });
    return `http://update.synology.com/autoupdate/whatsnew.php?${query.toString()}`;
  }

  private static arrayValues(valueArg: unknown): unknown[] {
    if (Array.isArray(valueArg)) {
      return valueArg;
    }
    if (valueArg && typeof valueArg === 'object') {
      return Object.values(valueArg);
    }
    return [];
  }

  private static recordValue(valueArg: unknown): Record<string, unknown> | undefined {
    return valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg) ? valueArg as Record<string, unknown> : undefined;
  }

  private static stringRecordValue(valueArg: unknown): Record<string, string> | undefined {
    const record = this.recordValue(valueArg);
    if (!record) {
      return undefined;
    }
    const stringRecord: Record<string, string> = {};
    for (const [key, value] of Object.entries(record)) {
      stringRecord[key] = typeof value === 'string' ? value : String(value);
    }
    return stringRecord;
  }

  private static firstString(recordsArg: Record<string, unknown>[], ...keysArg: string[]): string | undefined {
    for (const record of recordsArg) {
      for (const key of keysArg) {
        const value = this.stringValue(record[key]);
        if (value) {
          return value;
        }
      }
    }
    return undefined;
  }

  private static firstNumber(recordsArg: Record<string, unknown>[], ...keysArg: string[]): number | undefined {
    for (const record of recordsArg) {
      for (const key of keysArg) {
        const value = this.numberValue(record[key]);
        if (value !== undefined) {
          return value;
        }
      }
    }
    return undefined;
  }

  private static firstBoolean(recordsArg: Record<string, unknown>[], ...keysArg: string[]): boolean | undefined {
    for (const record of recordsArg) {
      for (const key of keysArg) {
        const value = this.booleanValue(record[key]);
        if (value !== undefined) {
          return value;
        }
      }
    }
    return undefined;
  }

  private static stringValue(valueArg: unknown): string | undefined {
    return typeof valueArg === 'string' && valueArg.trim() ? valueArg.trim() : undefined;
  }

  private static numberValue(valueArg: unknown): number | undefined {
    if (typeof valueArg === 'number' && Number.isFinite(valueArg)) {
      return valueArg;
    }
    if (typeof valueArg === 'string' && valueArg.trim()) {
      const parsed = Number(valueArg);
      return Number.isFinite(parsed) ? parsed : undefined;
    }
    return undefined;
  }

  private static booleanValue(valueArg: unknown): boolean | undefined {
    if (typeof valueArg === 'boolean') {
      return valueArg;
    }
    if (typeof valueArg === 'number') {
      return valueArg === 1 ? true : valueArg === 0 ? false : undefined;
    }
    if (typeof valueArg === 'string') {
      if (['true', '1', 'yes', 'on', 'enabled'].includes(valueArg.toLowerCase())) {
        return true;
      }
      if (['false', '0', 'no', 'off', 'disabled'].includes(valueArg.toLowerCase())) {
        return false;
      }
    }
    return undefined;
  }

  private static stringArray(valueArg: unknown): string[] {
    if (Array.isArray(valueArg)) {
      return valueArg.filter((itemArg): itemArg is string => typeof itemArg === 'string' && Boolean(itemArg.trim()));
    }
    const value = this.stringValue(valueArg);
    return value ? [value] : [];
  }

  private static uniqueStrings(valuesArg: string[]): string[] {
    return [...new Set(valuesArg.filter(Boolean))];
  }

  private static cleanAttributes<TValue extends Record<string, unknown>>(recordArg: TValue): TValue {
    const cleaned: Record<string, unknown> = {};
    for (const [key, value] of Object.entries(recordArg)) {
      if (value !== undefined) {
        cleaned[key] = value;
      }
    }
    return cleaned as TValue;
  }

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

  private static title(valueArg: string): string {
    return valueArg.replace(/_/g, ' ').replace(/\b\w/g, (characterArg) => characterArg.toUpperCase());
  }
}
