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 { IGogogate2Config } from './gogogate2.types.js';
import { gogogate2DefaultName, gogogate2Profile } from './gogogate2.types.js';

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

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

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

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

  private static normalizeRawData(configArg: IGogogate2Config, rawDataArg: unknown): unknown {
    if (isSnapshotRecord(rawDataArg)) {
      return rawDataArg;
    }

    const rawObject = isRecord(rawDataArg) ? rawDataArg : undefined;
    const doors = arrayValue(rawObject?.doors ?? rawObject?.door_statuses ?? rawObject?.doorStatuses);
    if (!doors.length) {
      return rawDataArg;
    }

    const entities: ISimpleLocalEntitySnapshot[] = [];
    for (const door of doors) {
      const doorId = stringValue(door.door_id ?? door.doorId ?? door.id) || String(entities.length + 1);
      const name = stringValue(door.name) || `Door ${doorId}`;
      const status = stringValue(door.status ?? door.door_status ?? door.doorStatus) || 'unknown';
      const entityId = `door_${SimpleLocalMapper.slug(doorId)}`;
      entities.push({
        id: entityId,
        name,
        platform: 'cover',
        state: status,
        available: true,
        writable: true,
        attributes: {
          doorId,
          deviceClass: booleanValue(door.gate) ? 'gate' : 'garage',
          isClosed: status === 'closed' || status === 'CLOSED',
          isOpening: status === 'opening' || status === 'OPENING',
          isClosing: status === 'closing' || status === 'CLOSING',
        },
      });

      const sensorId = stringValue(door.sensorid ?? door.sensorId);
      if (sensorId && sensorId !== 'WIRE') {
        const battery = numberValue(door.voltage ?? door.battery);
        const temperature = numberValue(door.temperature);
        if (battery !== undefined) {
          entities.push({
            id: `${entityId}_battery`,
            name: `${name} battery`,
            platform: 'sensor',
            state: battery,
            available: true,
            unit: '%',
            deviceClass: 'battery',
            stateClass: 'measurement',
            attributes: {
              doorId,
              sensorId,
              entityCategory: 'diagnostic',
            },
          });
        }
        if (temperature !== undefined) {
          entities.push({
            id: `${entityId}_temperature`,
            name: `${name} temperature`,
            platform: 'sensor',
            state: temperature,
            available: true,
            unit: 'C',
            deviceClass: 'temperature',
            stateClass: 'measurement',
            attributes: {
              doorId,
              sensorId,
            },
          });
        }
      }
    }

    const host = configArg.host || configArg.ipAddress || configArg.ip_address || stringValue(rawObject?.ip_address);
    const remoteAccess = stringValue(rawObject?.remoteaccess ?? rawObject?.remoteAccess);
    const remoteAccessEnabled = booleanValue(rawObject?.remoteaccessenabled ?? rawObject?.remoteAccessEnabled);
    const name = configArg.name || stringValue(rawObject?.gogogatename) || stringValue(rawObject?.ismartgatename) || gogogate2DefaultName;

    return {
      device: {
        id: configArg.uniqueId || remoteAccess || host || name,
        name,
        manufacturer: gogogate2Profile.manufacturer,
        model: stringValue(rawObject?.model) || gogogate2Profile.model,
        host,
        protocol: 'http',
        configurationUrl: remoteAccessEnabled && remoteAccess ? `https://${remoteAccess}` : host ? `http://${host}` : undefined,
        attributes: {
          firmwareVersion: stringValue(rawObject?.firmwareversion ?? rawObject?.firmwareVersion),
          deviceType: configArg.device || stringValue(rawObject?.device),
          remoteAccessEnabled,
        },
      },
      entities,
      online: configArg.online ?? true,
      updatedAt: new Date().toISOString(),
      source: 'manual',
      rawData: rawDataArg,
    } satisfies ISimpleLocalSnapshot;
  }
}

const isSnapshotRecord = (valueArg: unknown): valueArg is ISimpleLocalSnapshot => Boolean(valueArg && typeof valueArg === 'object' && 'device' in valueArg && 'entities' in valueArg);

const isRecord = (valueArg: unknown): valueArg is Record<string, unknown> => Boolean(valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg));

const arrayValue = (valueArg: unknown): Record<string, unknown>[] => Array.isArray(valueArg) ? valueArg.filter(isRecord) : [];

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' && valueArg.trim() ? Number(valueArg) : undefined;
  return value !== undefined && Number.isFinite(value) ? value : undefined;
};

const booleanValue = (valueArg: unknown): boolean | undefined => typeof valueArg === 'boolean' ? valueArg : undefined;
