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 { ILinksysSmartConfig, ILinksysSmartLiveRawData } from './linksys_smart.types.js';
import { linksysSmartDefaultPort, linksysSmartProfile } from './linksys_smart.types.js';

export class LinksysSmartMapper {
  public static toSnapshot(optionsArg: Omit<ISimpleLocalSnapshotOptions<ILinksysSmartConfig>, 'profile'>): ISimpleLocalSnapshot {
    if (isLinksysSmartLiveRawData(optionsArg.rawData)) {
      return this.liveSnapshot(optionsArg as ISimpleLocalSnapshotOptions<ILinksysSmartConfig>);
    }
    return SimpleLocalMapper.toSnapshot({ ...optionsArg, profile: linksysSmartProfile });
  }

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

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

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

  private static liveSnapshot(optionsArg: ISimpleLocalSnapshotOptions<ILinksysSmartConfig>): ISimpleLocalSnapshot {
    const rawData = optionsArg.rawData as ILinksysSmartLiveRawData;
    const host = optionsArg.config.host || hostFromUrl(optionsArg.config.url);
    const port = optionsArg.config.port || portFromUrl(optionsArg.config.url) || (host ? linksysSmartDefaultPort : undefined);
    const online = optionsArg.online ?? true;
    return {
      device: {
        id: optionsArg.config.uniqueId || optionsArg.config.deviceId || (host ? `${host}:${port || ''}` : 'linksys_smart'),
        name: optionsArg.config.name || 'Linksys Smart Wi-Fi',
        manufacturer: 'Linksys',
        model: 'Smart Wi-Fi router',
        host,
        port,
        protocol: 'http',
        configurationUrl: configurationUrl(optionsArg.config),
      },
      entities: this.liveEntities(rawData, online),
      online,
      updatedAt: rawData.fetchedAt || new Date().toISOString(),
      source: optionsArg.source || 'http',
      rawData,
      error: optionsArg.error,
      capabilities: {
        localRead: true,
        localControl: false,
        liveControlRequiresExecutor: true,
      },
    };
  }

  private static liveEntities(rawDataArg: ILinksysSmartLiveRawData, onlineArg: boolean): ISimpleLocalEntitySnapshot[] {
    const devices = Array.isArray(rawDataArg.devices) ? rawDataArg.devices : [];
    const entities: ISimpleLocalEntitySnapshot[] = [{
      id: 'connected_devices',
      name: 'Connected devices',
      platform: 'sensor',
      state: devices.length,
      unit: 'devices',
      stateClass: 'total',
      available: onlineArg,
    }];
    for (const device of devices) {
      entities.push({
        id: `presence_${this.slug(device.macAddress)}`,
        uniqueId: `linksys_smart_${this.slug(device.macAddress)}`,
        name: device.name,
        platform: 'binary_sensor',
        state: true,
        available: onlineArg,
        deviceClass: 'presence',
        attributes: cleanAttributes({ macAddress: device.macAddress, deviceId: device.deviceId }),
      });
    }
    return entities;
  }
}

const isLinksysSmartLiveRawData = (valueArg: unknown): valueArg is ILinksysSmartLiveRawData => {
  return Boolean(valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg) && ('devices' in valueArg || 'rawResponse' in valueArg));
};

const hostFromUrl = (urlArg?: string): string | undefined => parsedUrl(urlArg)?.hostname;

const portFromUrl = (urlArg?: string): number | undefined => {
  const port = parsedUrl(urlArg)?.port;
  return port ? Number(port) : undefined;
};

const parsedUrl = (urlArg?: string): URL | undefined => {
  if (!urlArg) {
    return undefined;
  }
  try {
    return new URL(urlArg);
  } catch {
    return undefined;
  }
};

const configurationUrl = (configArg: ILinksysSmartConfig): string | undefined => {
  if (configArg.url) {
    const parsed = parsedUrl(configArg.url);
    if (parsed) {
      parsed.pathname = '';
      parsed.search = '';
      parsed.hash = '';
      return parsed.toString().replace(/\/$/, '');
    }
    return configArg.url.replace(/\/+$/, '');
  }
  if (!configArg.host) {
    return undefined;
  }
  const port = configArg.port || linksysSmartDefaultPort;
  return `http://${configArg.host}${port === 80 ? '' : `:${port}`}`;
};

const cleanAttributes = (attributesArg: Record<string, unknown>): Record<string, unknown> | undefined => {
  const clean = Object.fromEntries(Object.entries(attributesArg).filter(([, valueArg]) => valueArg !== undefined));
  return Object.keys(clean).length ? clean : undefined;
};
