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 { IHlkSw16Config } from './hlk_sw16.types.js';
import { hlkSw16DefaultName, hlkSw16DefaultPort, hlkSw16Profile } from './hlk_sw16.types.js';

const relayIds = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];

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

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

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

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

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

    const states = relayStateMap(rawDataArg);
    if (!states) {
      return rawDataArg;
    }

    const rawRecord = recordValue(rawDataArg);
    const host = configArg.host || stringValue(rawRecord?.host);
    const port = configArg.port || numberValue(rawRecord?.port) || hlkSw16DefaultPort;
    const id = configArg.uniqueId || (host ? `${host}:${port}` : undefined) || hlkSw16Profile.domain;
    const name = configArg.name || stringValue(rawRecord?.name) || hlkSw16DefaultName;
    const entities = relayIds.map((relayIdArg) => relayEntity(configArg, id, relayIdArg, states[relayIdArg]));

    return {
      device: {
        id,
        name,
        manufacturer: hlkSw16Profile.manufacturer,
        model: hlkSw16Profile.model,
        host,
        port,
        protocol: hlkSw16Profile.defaultProtocol,
        attributes: {
          relayCount: relayIds.length,
          keepAliveIntervalSeconds: 3,
          reconnectIntervalSeconds: 10,
        },
      },
      entities,
      online: configArg.online ?? true,
      updatedAt: new Date().toISOString(),
      source: 'manual',
      rawData: rawDataArg,
    } satisfies ISimpleLocalSnapshot;
  }
}

const relayEntity = (configArg: IHlkSw16Config, hubIdArg: string, relayIdArg: string, stateArg: boolean | undefined): ISimpleLocalEntitySnapshot => {
  const configured = configArg.switches?.[relayIdArg];
  const configuredName = typeof configured === 'string' ? configured : isRecord(configured) ? stringValue(configured.name) : undefined;
  return {
    id: `relay_${relayIdArg}`,
    uniqueId: `${hlkSw16Profile.domain}_${SimpleLocalMapper.slug(hubIdArg)}_relay_${relayIdArg}`,
    name: configuredName || `Relay ${relayIdArg.toUpperCase()}`,
    platform: 'switch',
    state: stateArg ?? false,
    available: stateArg !== undefined,
    writable: true,
    attributes: {
      relayId: relayIdArg,
      relayNumber: Number.parseInt(relayIdArg, 16),
    },
  };
};

const relayStateMap = (rawDataArg: unknown): Record<string, boolean | undefined> | undefined => {
  if (Array.isArray(rawDataArg)) {
    const states: Record<string, boolean | undefined> = {};
    rawDataArg.slice(0, relayIds.length).forEach((valueArg, indexArg) => {
      states[relayIds[indexArg]] = booleanValue(valueArg);
    });
    return states;
  }

  const rawRecord = recordValue(rawDataArg);
  if (!rawRecord) {
    return undefined;
  }

  const stateSource = rawRecord.relays ?? rawRecord.switches ?? rawRecord.states ?? rawRecord.status ?? rawRecord;
  if (Array.isArray(stateSource)) {
    return relayStateMap(stateSource);
  }
  if (!isRecord(stateSource)) {
    return undefined;
  }

  const states: Record<string, boolean | undefined> = {};
  let matched = false;
  for (const relayId of relayIds) {
    const value = stateSource[relayId] ?? stateSource[relayId.toUpperCase()] ?? stateSource[`relay_${relayId}`] ?? stateSource[`relay${relayId}`];
    if (value !== undefined) {
      matched = true;
      states[relayId] = booleanValue(value);
    }
  }
  return matched ? states : 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', 'on', 'yes'].includes(value)) {
    return true;
  }
  if (['0', 'false', 'off', 'no'].includes(value)) {
    return false;
  }
  return undefined;
};

const numberValue = (valueArg: unknown): number | undefined => typeof valueArg === 'number' && Number.isFinite(valueArg) ? valueArg : typeof valueArg === 'string' && Number.isFinite(Number(valueArg)) ? Number(valueArg) : 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;
