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 { IPanasonicVieraConfig, IPanasonicVieraRawApiData, TPanasonicVieraRawData } from './panasonic_viera.types.js';
import { panasonicVieraDefaultName, panasonicVieraDefaultPort, panasonicVieraProfile } from './panasonic_viera.types.js';

export class PanasonicVieraMapper {
  public static toSnapshot(optionsArg: Omit<ISimpleLocalSnapshotOptions<IPanasonicVieraConfig>, 'profile'>): ISimpleLocalSnapshot {
    return SimpleLocalMapper.toSnapshot({ ...optionsArg, profile: panasonicVieraProfile, rawData: this.normalizeRawData(optionsArg.rawData, optionsArg.config) });
  }

  public static toSnapshotFromRaw(configArg: IPanasonicVieraConfig, rawDataArg: TSimpleLocalRawData | TPanasonicVieraRawData): ISimpleLocalSnapshot {
    return this.toSnapshot({ config: configArg, rawData: rawDataArg, online: true, source: 'manual' });
  }

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

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

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

  public static normalizeRawData(rawDataArg: unknown, configArg: IPanasonicVieraConfig): unknown {
    const parsedRawData = parseMaybeJson(rawDataArg);
    const rawObject = objectValue(parsedRawData);
    if (!rawObject || rawObject.device || Array.isArray(rawObject.entities)) {
      return parsedRawData;
    }

    const deviceInfo = objectValue(rawObject.deviceInfo);
    if (!deviceInfo && rawObject.volume === undefined && rawObject.muted === undefined) {
      return parsedRawData;
    }

    const host = configArg.host || hostFromUrl(configArg.url);
    const port = configArg.port || (host ? panasonicVieraDefaultPort : undefined);
    const name = configArg.name || stringValue(deviceInfo?.friendlyName) || stringValue(deviceInfo?.FriendlyName) || panasonicVieraDefaultName;
    const manufacturer = stringValue(deviceInfo?.manufacturer) || stringValue(deviceInfo?.Manufacturer) || 'Panasonic';
    const model = stringValue(deviceInfo?.modelNumber) || stringValue(deviceInfo?.modelName) || stringValue(deviceInfo?.ModelNumber) || 'Panasonic Viera';
    const udn = stringValue(deviceInfo?.UDN) || stringValue(deviceInfo?.udn);
    const volume = numberValue(rawObject.volume);
    const muted = booleanValue(rawObject.muted);
    const entities: ISimpleLocalEntitySnapshot[] = [
      {
        id: 'media_player',
        name,
        platform: 'media_player',
        state: rawObject.state || 'on',
        writable: true,
        attributes: cleanAttributes({
          volumeLevel: volume === undefined ? undefined : volume / 100,
          volumePercent: volume,
          muted,
          tvType: rawObject.tvType,
        }),
      },
      {
        id: 'remote',
        name: `${name} Remote`,
        platform: 'button',
        state: false,
        writable: true,
        attributes: { action: 'press' },
      },
    ];

    if (volume !== undefined) {
      entities.push({
        id: 'volume',
        name: 'Volume',
        platform: 'sensor',
        state: volume,
        unit: '%',
        deviceClass: 'volume',
        attributes: { source: 'RenderingControl.GetVolume' },
      });
    }
    if (muted !== undefined) {
      entities.push({
        id: 'muted',
        name: 'Muted',
        platform: 'binary_sensor',
        state: muted,
        attributes: { source: 'RenderingControl.GetMute' },
      });
    }

    return {
      device: {
        id: configArg.uniqueId || configArg.deviceId || udn || (host ? `${host}:${port || panasonicVieraDefaultPort}` : undefined) || panasonicVieraDefaultName,
        name,
        manufacturer,
        model,
        serialNumber: udn,
        host,
        port,
        protocol: 'http',
        attributes: cleanAttributes({ udn, rawDeviceInfo: deviceInfo }),
      },
      entities,
      online: true,
      updatedAt: stringValue(rawObject.fetchedAt) || new Date().toISOString(),
      source: 'http',
      rawData: parsedRawData,
    } satisfies ISimpleLocalSnapshot;
  }
}

const parseMaybeJson = (valueArg: unknown): unknown => {
  if (typeof valueArg !== 'string') {
    return valueArg;
  }
  try {
    return JSON.parse(valueArg) as IPanasonicVieraRawApiData;
  } catch {
    return valueArg;
  }
};

const objectValue = (valueArg: unknown): Record<string, unknown> | undefined => {
  return valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg) ? valueArg as Record<string, unknown> : undefined;
};

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

const numberValue = (valueArg: unknown): number | undefined => {
  if (typeof valueArg === 'number' && Number.isFinite(valueArg)) {
    return valueArg;
  }
  if (typeof valueArg === 'string' && valueArg.trim() && Number.isFinite(Number(valueArg))) {
    return Number(valueArg);
  }
  return undefined;
};

const booleanValue = (valueArg: unknown): boolean | undefined => {
  if (typeof valueArg === 'boolean') {
    return valueArg;
  }
  if (typeof valueArg === 'number') {
    return valueArg !== 0;
  }
  if (typeof valueArg === 'string') {
    if (valueArg === '1' || valueArg.toLowerCase() === 'true') {
      return true;
    }
    if (valueArg === '0' || valueArg.toLowerCase() === 'false') {
      return false;
    }
  }
  return undefined;
};

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

const hostFromUrl = (urlArg: string | undefined): string | undefined => {
  if (!urlArg) {
    return undefined;
  }
  try {
    return new URL(urlArg.includes('://') ? urlArg : `http://${urlArg}`).hostname;
  } catch {
    return undefined;
  }
};
