import type * as shxInterfaces from '@smarthome.exchange/interfaces';
import { SimpleLocalMapper, type IIntegrationEntity, type ISimpleLocalEntitySnapshot, type ISimpleLocalSnapshot, type ISimpleLocalSnapshotOptions } from '../../core/index.js';
import type { IMutesyncConfig, IMutesyncNativeRawData, IMutesyncStateResponse, TMutesyncRawData } from './mutesync.types.js';
import { mutesyncDefaultName, mutesyncDefaultPort, mutesyncProfile } from './mutesync.types.js';

export class MutesyncMapper {
  public static toSnapshot(optionsArg: Omit<ISimpleLocalSnapshotOptions<IMutesyncConfig>, 'profile'>): ISimpleLocalSnapshot {
    const rawData = optionsArg.rawData ?? optionsArg.config.rawData;
    if (this.isNativeRawData(rawData)) {
      return this.toSnapshotFromNativeRaw(optionsArg.config, rawData, optionsArg.online ?? true, optionsArg.source || 'http', optionsArg.error);
    }
    return SimpleLocalMapper.toSnapshot({ ...optionsArg, profile: mutesyncProfile });
  }

  public static toSnapshotFromRaw(configArg: IMutesyncConfig, rawDataArg: TMutesyncRawData): ISimpleLocalSnapshot {
    if (this.isNativeRawData(rawDataArg)) {
      return this.toSnapshotFromNativeRaw(configArg, rawDataArg, true, 'manual');
    }
    return SimpleLocalMapper.toSnapshot({ profile: mutesyncProfile, config: configArg, rawData: rawDataArg, online: true, source: 'manual' });
  }

  public static toSnapshotFromNativeRaw(configArg: IMutesyncConfig, rawDataArg: IMutesyncNativeRawData, onlineArg = true, sourceArg: ISimpleLocalSnapshot['source'] = 'http', errorArg?: string): ISimpleLocalSnapshot {
    const state = rawDataArg.state;
    const endpoint = endpointFromConfig(configArg);
    const userId = stringValue(state['user-id']) || configArg.uniqueId || configArg.deviceId || endpoint.host || mutesyncDefaultName;
    const entities: ISimpleLocalEntitySnapshot[] = [
      {
        id: 'in_meeting',
        uniqueId: `${mutesyncProfile.domain}_${this.slug(userId)}_in_meeting`,
        name: 'In meeting',
        platform: 'binary_sensor',
        state: Boolean(state.in_meeting),
        available: onlineArg,
        writable: false,
      },
      {
        id: 'muted',
        uniqueId: `${mutesyncProfile.domain}_${this.slug(userId)}_muted`,
        name: 'Muted',
        platform: 'binary_sensor',
        state: Boolean(state.muted),
        available: onlineArg,
        writable: false,
      },
    ];

    return {
      device: {
        id: configArg.uniqueId || configArg.deviceId || userId,
        name: configArg.name || mutesyncDefaultName,
        manufacturer: 'mutesync',
        model: 'mutesync app',
        host: endpoint.host,
        port: endpoint.port,
        protocol: endpoint.protocol,
        configurationUrl: endpoint.host ? `${endpoint.protocol}://${endpoint.host}${endpoint.port && endpoint.port !== mutesyncDefaultPort ? `:${endpoint.port}` : ''}` : undefined,
        attributes: cleanAttributes({ userId }),
      },
      entities,
      online: onlineArg,
      updatedAt: rawDataArg.fetchedAt || new Date().toISOString(),
      source: sourceArg,
      rawData: rawDataArg,
      error: errorArg,
      capabilities: {
        localRead: true,
        localControl: false,
        protocol: 'mutesync-http',
      },
    };
  }

  public static toDevices(snapshotArg: ISimpleLocalSnapshot): shxInterfaces.data.IDeviceDefinition[] {
    return SimpleLocalMapper.toDevices(mutesyncProfile, snapshotArg);
  }

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

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

  public static isNativeRawData(valueArg: unknown): valueArg is IMutesyncNativeRawData {
    if (!valueArg || typeof valueArg !== 'object' || Array.isArray(valueArg)) {
      return false;
    }
    const rawObject = valueArg as Record<string, unknown>;
    return Boolean(rawObject.state && typeof rawObject.state === 'object' && !Array.isArray(rawObject.state) && (stateValue(rawObject)?.muted !== undefined || stateValue(rawObject)?.in_meeting !== undefined));
  }
}

const stateValue = (valueArg: Record<string, unknown>): IMutesyncStateResponse | undefined => {
  if (valueArg.state && typeof valueArg.state === 'object' && !Array.isArray(valueArg.state)) {
    return valueArg.state as IMutesyncStateResponse;
  }
  if ('muted' in valueArg || 'in_meeting' in valueArg) {
    return valueArg as IMutesyncStateResponse;
  }
  return undefined;
};

const endpointFromConfig = (configArg: IMutesyncConfig): { host?: string; port: number; protocol: 'http' | 'https' } => {
  const configured = stringValue(configArg.url) || stringValue(configArg.host);
  if (configured && /^https?:\/\//i.test(configured)) {
    try {
      const url = new URL(configured);
      return { host: url.hostname, port: configArg.port || (url.port ? Number(url.port) : mutesyncDefaultPort), protocol: url.protocol === 'https:' ? 'https' : 'http' };
    } catch {
      return { host: configured, port: configArg.port || mutesyncDefaultPort, protocol: 'http' };
    }
  }
  return { host: configured, port: configArg.port || mutesyncDefaultPort, protocol: configArg.useTls ? 'https' : 'http' };
};

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

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