import * as plugins from '../../plugins.js';
import type { IIntegrationEntity } from '../../core/types.js';
import type { IDlnaDmsContentObject, IDlnaDmsSnapshot } from './dlna_dms.types.js';
import { dlnaDmsDomain } from './dlna_dms.types.js';

export class DlnaDmsMapper {
  public static toDevices(snapshotArg: IDlnaDmsSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const updatedAt = snapshotArg.updatedAt || new Date().toISOString();
    const features: plugins.shxInterfaces.data.IDeviceFeature[] = [
      { id: 'server_status', capability: 'sensor', name: 'Server status', readable: true, writable: false },
      { id: 'content_directory', capability: 'sensor', name: 'Content directory', readable: true, writable: false },
      { id: 'library_count', capability: 'sensor', name: 'Library count', readable: true, writable: false },
      { id: 'content_item_count', capability: 'sensor', name: 'Content item count', readable: true, writable: false },
      { id: 'system_update_id', capability: 'sensor', name: 'System update ID', readable: true, writable: false },
    ];
    const state: plugins.shxInterfaces.data.IDeviceState[] = [
      { featureId: 'server_status', value: snapshotArg.online ? 'online' : 'offline', updatedAt },
      { featureId: 'content_directory', value: snapshotArg.status.contentDirectoryAvailable ? 'available' : 'unavailable', updatedAt },
      { featureId: 'library_count', value: snapshotArg.libraries.length, updatedAt },
      { featureId: 'content_item_count', value: this.contentItemCount(snapshotArg), updatedAt },
      { featureId: 'system_update_id', value: snapshotArg.status.systemUpdateId ?? null, updatedAt },
    ];

    for (const library of snapshotArg.libraries) {
      const featureId = `library_${this.slug(library.id || library.title)}`;
      features.push({ id: featureId, capability: 'sensor', name: library.title, readable: true, writable: false });
      state.push({ featureId, value: library.childCount ?? null, updatedAt });
    }

    return [{
      id: this.serverDeviceId(snapshotArg),
      integrationDomain: dlnaDmsDomain,
      name: this.serverName(snapshotArg),
      protocol: 'http',
      manufacturer: snapshotArg.device.manufacturer || 'DLNA',
      model: snapshotArg.device.modelName || snapshotArg.device.modelNumber || 'Digital Media Server',
      online: snapshotArg.online,
      features,
      state,
      metadata: this.cleanAttributes({
        udn: snapshotArg.device.udn,
        usn: snapshotArg.device.usn,
        deviceType: snapshotArg.device.deviceType,
        location: snapshotArg.device.location,
        sourceId: snapshotArg.device.sourceId,
        source: snapshotArg.source,
        serialNumber: snapshotArg.device.serialNumber,
        contentDirectoryAvailable: snapshotArg.status.contentDirectoryAvailable,
        connectionManagerAvailable: snapshotArg.status.connectionManagerAvailable,
        sourceProtocolInfo: snapshotArg.status.sourceProtocolInfo,
        sinkProtocolInfo: snapshotArg.status.sinkProtocolInfo,
        searchCapabilities: snapshotArg.status.searchCapabilities,
        sortCapabilities: snapshotArg.status.sortCapabilities,
        error: snapshotArg.error,
      }),
    }];
  }

  public static toEntities(snapshotArg: IDlnaDmsSnapshot): IIntegrationEntity[] {
    const deviceId = this.serverDeviceId(snapshotArg);
    const uniqueBase = this.uniqueBase(snapshotArg);
    const serverName = this.serverName(snapshotArg);
    const entities: IIntegrationEntity[] = [
      this.entity('sensor', `${serverName} DLNA DMS Status`, 'status', snapshotArg.online ? 'online' : 'offline', deviceId, uniqueBase, true, {
        deviceClass: 'enum',
        options: ['online', 'offline'],
        source: snapshotArg.source,
        location: snapshotArg.device.location,
        udn: snapshotArg.device.udn,
        usn: snapshotArg.device.usn,
        contentDirectoryAvailable: snapshotArg.status.contentDirectoryAvailable,
        connectionManagerAvailable: snapshotArg.status.connectionManagerAvailable,
        error: snapshotArg.error || snapshotArg.status.error,
      }),
      this.entity('sensor', `${serverName} DLNA DMS Libraries`, 'libraries', snapshotArg.libraries.length, deviceId, uniqueBase, snapshotArg.online, {
        stateClass: 'measurement',
        libraries: snapshotArg.libraries.map((libraryArg) => this.contentSummary(libraryArg)),
      }),
      this.entity('sensor', `${serverName} DLNA DMS Content Directory`, 'content_directory', this.contentItemCount(snapshotArg), deviceId, uniqueBase, snapshotArg.online, {
        stateClass: 'measurement',
        rootObjectId: snapshotArg.contentDirectory.root?.id,
        rootTitle: snapshotArg.contentDirectory.root?.title,
        totalMatches: snapshotArg.contentDirectory.totalMatches,
        numberReturned: snapshotArg.contentDirectory.numberReturned,
        updateId: snapshotArg.contentDirectory.updateId,
        systemUpdateId: snapshotArg.status.systemUpdateId,
        searchCapabilities: snapshotArg.status.searchCapabilities,
        sortCapabilities: snapshotArg.status.sortCapabilities,
        children: snapshotArg.contentDirectory.children.map((childArg) => this.contentSummary(childArg)),
      }),
    ];

    for (const library of snapshotArg.libraries) {
      entities.push(this.entity('sensor', `${serverName} ${library.title} DLNA Library`, `library_${this.slug(library.id || library.title)}`, library.childCount ?? null, deviceId, uniqueBase, snapshotArg.online, {
        objectId: library.id,
        parentId: library.parentId,
        upnpClass: library.upnpClass,
        mediaClass: library.mediaClass,
        canExpand: library.canExpand,
        thumbnail: library.thumbnail,
      }));
    }

    return entities;
  }

  public static serverDeviceId(snapshotArg: IDlnaDmsSnapshot): string {
    return `dlna_dms.server.${this.uniqueBase(snapshotArg)}`;
  }

  public static slug(valueArg: string): string {
    return valueArg.toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '') || 'dlna_dms';
  }

  private static entity(platformArg: IIntegrationEntity['platform'], nameArg: string, keyArg: string, stateArg: unknown, deviceIdArg: string, uniqueBaseArg: string, availableArg: boolean, attributesArg: Record<string, unknown>): IIntegrationEntity {
    return {
      id: `${platformArg}.${this.slug(nameArg)}`,
      uniqueId: `dlna_dms_${uniqueBaseArg}_${keyArg}`,
      integrationDomain: dlnaDmsDomain,
      deviceId: deviceIdArg,
      platform: platformArg,
      name: nameArg,
      state: stateArg,
      attributes: this.cleanAttributes(attributesArg),
      available: availableArg,
    };
  }

  private static contentSummary(objectArg: IDlnaDmsContentObject): Record<string, unknown> {
    return this.cleanAttributes({
      id: objectArg.id,
      title: objectArg.title,
      type: objectArg.type,
      upnpClass: objectArg.upnpClass,
      mediaClass: objectArg.mediaClass,
      childCount: objectArg.childCount,
      canPlay: objectArg.canPlay,
      canExpand: objectArg.canExpand,
      resourceCount: objectArg.resources.length,
      thumbnail: objectArg.thumbnail,
    });
  }

  private static contentItemCount(snapshotArg: IDlnaDmsSnapshot): number {
    return snapshotArg.contentDirectory.totalMatches ?? snapshotArg.contentDirectory.children.length;
  }

  private static uniqueBase(snapshotArg: IDlnaDmsSnapshot): string {
    return this.slug(snapshotArg.device.udn || snapshotArg.device.usn || snapshotArg.device.location || this.serverName(snapshotArg));
  }

  private static serverName(snapshotArg: IDlnaDmsSnapshot): string {
    return snapshotArg.device.friendlyName || 'DLNA Digital Media Server';
  }

  private static cleanAttributes(attributesArg: Record<string, unknown>): Record<string, unknown> {
    return Object.fromEntries(Object.entries(attributesArg).filter(([, valueArg]) => valueArg !== undefined));
  }
}
