import type * as shxInterfaces from '@smarthome.exchange/interfaces';
import { SimpleLocalMapper, type IIntegrationEntity, type ISimpleLocalSnapshot, type ISimpleLocalSnapshotOptions, type TSimpleLocalRawData } from '../../core/index.js';
import type { IMediaroomConfig } from './mediaroom.types.js';
import { mediaroomDefaultPort, mediaroomProfile } from './mediaroom.types.js';

export class MediaroomMapper {
  public static toSnapshot(optionsArg: Omit<ISimpleLocalSnapshotOptions<IMediaroomConfig>, 'profile'>): ISimpleLocalSnapshot {
    if (this.isMediaroomRawData(optionsArg.rawData) || (optionsArg.config.host && optionsArg.source === 'tcp')) {
      return this.toMediaroomSnapshot(optionsArg.config, recordValue(optionsArg.rawData) || {}, optionsArg.online, optionsArg.source, optionsArg.error);
    }
    return SimpleLocalMapper.toSnapshot({ ...optionsArg, profile: mediaroomProfile });
  }

  public static toSnapshotFromRaw(configArg: IMediaroomConfig, rawDataArg: TSimpleLocalRawData): ISimpleLocalSnapshot {
    return SimpleLocalMapper.toSnapshot({ profile: mediaroomProfile, config: configArg, rawData: rawDataArg, online: true, source: 'manual' });
  }

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

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

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

  private static toMediaroomSnapshot(configArg: IMediaroomConfig, rawDataArg: Record<string, unknown>, onlineArg = true, sourceArg: ISimpleLocalSnapshot['source'] = 'tcp', errorArg?: string): ISimpleLocalSnapshot {
    const endpoint = parseEndpoint(configArg.url || configArg.host, configArg.port);
    const state = stringValue(rawDataArg.state) || (configArg.optimistic && onlineArg ? 'playing' : 'unknown');
    const name = configArg.name || endpoint?.host || mediaroomProfile.defaultName || mediaroomProfile.displayName;

    return {
      device: {
        id: configArg.uniqueId || configArg.deviceId || stringValue(rawDataArg.deviceId) || (endpoint?.host ? `${endpoint.host}:${endpoint.port}` : mediaroomProfile.domain),
        name: `Mediaroom ${name}`,
        manufacturer: mediaroomProfile.manufacturer,
        model: mediaroomProfile.model,
        host: endpoint?.host,
        port: endpoint?.port,
        protocol: 'tcp',
        configurationUrl: endpoint ? `tcp://${endpoint.host}:${endpoint.port}` : undefined,
        attributes: cleanAttributes({ optimistic: configArg.optimistic, source: sourceArg, ...configArg.metadata }),
      },
      entities: [{
        id: 'media_player',
        name: 'Mediaroom media player',
        platform: 'media_player',
        state,
        available: onlineArg,
        writable: true,
        attributes: cleanAttributes({
          key: 'media_player',
          lastCommand: rawDataArg.lastCommand,
          supportedCommands: ['Power', 'PlayPause', 'Stop', 'ProgUp', 'ProgDown', 'VolUp', 'VolDown', 'Mute'],
          source: sourceArg,
        }),
      }],
      online: onlineArg,
      updatedAt: stringValue(rawDataArg.fetchedAt) || new Date().toISOString(),
      source: sourceArg,
      rawData: rawDataArg,
      error: errorArg,
      capabilities: { localRead: true, localControl: true, liveControlRequiresExecutor: false },
    };
  }

  private static isMediaroomRawData(valueArg: unknown): valueArg is Record<string, unknown> {
    return Boolean(valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg) && ('state' in valueArg || 'lastCommand' in valueArg || 'deviceId' in valueArg));
  }
}

const parseEndpoint = (valueArg: string | undefined, portArg?: number): { host: string; port: number } | undefined => {
  if (!valueArg) {
    return undefined;
  }
  try {
    const url = new URL(/^[a-z][a-z0-9+.-]*:\/\//i.test(valueArg) ? valueArg : `tcp://${valueArg}`);
    return { host: url.hostname, port: portArg || (url.port ? Number(url.port) : mediaroomDefaultPort) };
  } catch {
    return { host: valueArg, port: portArg || mediaroomDefaultPort };
  }
};

const recordValue = (valueArg: unknown): Record<string, unknown> | undefined => valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg) ? valueArg as Record<string, unknown> : undefined;
const stringValue = (valueArg: unknown): string | undefined => typeof valueArg === 'string' && valueArg.trim() ? valueArg.trim() : undefined;
const cleanAttributes = (valueArg: Record<string, unknown>): Record<string, unknown> | undefined => {
  const clean = Object.fromEntries(Object.entries(valueArg).filter(([, entryArg]) => entryArg !== undefined));
  return Object.keys(clean).length ? clean : undefined;
};
