import * as plugins from '../../plugins.js';
import type { IIntegrationEntity } from '../../core/types.js';
import type { IBraviatvSnapshot, IBraviatvState } from './braviatv.types.js';

export class BraviatvMapper {
  public static toDevices(snapshotArg: IBraviatvSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const state = this.state(snapshotArg);
    const updatedAt = snapshotArg.updatedAt || new Date().toISOString();
    return [{
      id: this.deviceId(snapshotArg),
      integrationDomain: 'braviatv',
      name: this.deviceName(snapshotArg),
      protocol: 'http',
      manufacturer: 'Sony',
      model: snapshotArg.systemInfo.model,
      online: state.available !== false && state.power !== 'off',
      features: [
        { id: 'power', capability: 'media', name: 'Power', readable: true, writable: true },
        { id: 'playback', capability: 'media', name: 'Playback', readable: true, writable: true },
        { id: 'source', capability: 'media', name: 'Source', readable: true, writable: true },
        { id: 'volume', capability: 'media', name: 'Volume', readable: true, writable: true, unit: '%' },
        { id: 'muted', capability: 'media', name: 'Muted', readable: true, writable: true },
        { id: 'remote_command', capability: 'media', name: 'Remote command', readable: false, writable: true },
      ],
      state: [
        { featureId: 'power', value: state.power, updatedAt },
        { featureId: 'playback', value: state.playback, updatedAt },
        { featureId: 'source', value: this.source(snapshotArg) || null, updatedAt },
        { featureId: 'volume', value: state.volumePercent ?? null, updatedAt },
        { featureId: 'muted', value: state.muted ?? null, updatedAt },
      ],
      metadata: {
        cid: snapshotArg.systemInfo.cid,
        serialNumber: snapshotArg.systemInfo.serial,
        macAddress: snapshotArg.systemInfo.macAddr,
        generation: snapshotArg.systemInfo.generation,
        sources: snapshotArg.sources.map((sourceArg) => ({ title: sourceArg.title, uri: sourceArg.uri, type: sourceArg.type })),
        apps: snapshotArg.apps.map((appArg) => ({ title: appArg.title, uri: appArg.uri, icon: appArg.icon })),
      },
    }];
  }

  public static toEntities(snapshotArg: IBraviatvSnapshot): IIntegrationEntity[] {
    const state = this.state(snapshotArg);
    return [{
      id: `media_player.${this.slug(this.deviceName(snapshotArg))}`,
      uniqueId: `braviatv_${this.slug(this.identity(snapshotArg))}`,
      integrationDomain: 'braviatv',
      deviceId: this.deviceId(snapshotArg),
      platform: 'media_player',
      name: this.deviceName(snapshotArg),
      state: this.mediaState(state),
      attributes: {
        source: this.source(snapshotArg),
        sourceUri: state.sourceUri,
        sourceList: this.sourceList(snapshotArg),
        volumeLevel: state.volumeLevel,
        isVolumeMuted: state.muted,
        mediaTitle: state.mediaTitle,
        mediaChannel: state.mediaChannel,
        mediaContentId: state.mediaContentId,
        mediaContentType: state.mediaContentType,
        mediaDuration: state.mediaDuration,
        mediaPosition: state.mediaPosition,
        mediaPositionUpdatedAt: state.mediaPositionUpdatedAt,
        model: snapshotArg.systemInfo.model,
        powerStatus: state.powerStatus,
      },
      available: state.available !== false,
    }];
  }

  public static deviceId(snapshotArg: IBraviatvSnapshot): string {
    return `braviatv.device.${this.slug(this.identity(snapshotArg))}`;
  }

  private static state(snapshotArg: IBraviatvSnapshot): IBraviatvState & Required<Pick<IBraviatvState, 'power' | 'playback'>> {
    const state = snapshotArg.state || {};
    const power = state.power || this.powerFromStatus(state.powerStatus);
    return {
      ...state,
      power,
      playback: state.playback || (power === 'off' ? 'off' : 'idle'),
      volumePercent: typeof state.volumePercent === 'number' ? state.volumePercent : typeof state.volumeLevel === 'number' ? Math.round(state.volumeLevel * 100) : undefined,
    };
  }

  private static mediaState(stateArg: IBraviatvState): string {
    if (stateArg.available === false) {
      return 'unavailable';
    }
    if (stateArg.power === 'off') {
      return 'off';
    }
    if (stateArg.playback === 'playing') {
      return 'playing';
    }
    if (stateArg.playback === 'paused') {
      return 'paused';
    }
    if (stateArg.power === 'unknown') {
      return 'unknown';
    }
    return stateArg.source || stateArg.mediaTitle ? 'on' : 'idle';
  }

  private static source(snapshotArg: IBraviatvSnapshot): string | undefined {
    return snapshotArg.state.source || this.sourceTitle(snapshotArg, snapshotArg.state.sourceUri) || snapshotArg.state.appTitle;
  }

  private static sourceList(snapshotArg: IBraviatvSnapshot): string[] {
    const values = [...snapshotArg.sources, ...snapshotArg.apps, ...(snapshotArg.channels || [])]
      .map((sourceArg) => sourceArg.title)
      .filter(Boolean);
    return [...new Set(values)];
  }

  private static sourceTitle(snapshotArg: IBraviatvSnapshot, uriArg: string | undefined): string | undefined {
    if (!uriArg) {
      return undefined;
    }
    return [...snapshotArg.sources, ...snapshotArg.apps, ...(snapshotArg.channels || [])].find((sourceArg) => sourceArg.uri === uriArg)?.title;
  }

  private static powerFromStatus(statusArg: string | undefined): 'on' | 'off' | 'unknown' {
    const status = (statusArg || '').toLowerCase();
    if (status === 'active') {
      return 'on';
    }
    if (status === 'standby' || status === 'off') {
      return 'off';
    }
    return 'unknown';
  }

  private static deviceName(snapshotArg: IBraviatvSnapshot): string {
    return snapshotArg.systemInfo.name || snapshotArg.systemInfo.model || 'Sony Bravia TV';
  }

  private static identity(snapshotArg: IBraviatvSnapshot): string {
    return snapshotArg.systemInfo.cid || snapshotArg.systemInfo.macAddr || snapshotArg.systemInfo.serial || this.deviceName(snapshotArg);
  }

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