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 { IFilesizeConfig } from './filesize.types.js';
import { filesizeDefaultName, filesizeProfile } from './filesize.types.js';

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

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

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

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

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

  private static normalizeRawData(configArg: IFilesizeConfig, rawDataArg: unknown): unknown {
    if (isRecord(rawDataArg) && 'device' in rawDataArg && 'entities' in rawDataArg) {
      return rawDataArg;
    }

    const rawObject = isRecord(rawDataArg) ? rawDataArg : undefined;
    const metadata = configArg.metadata || {};
    const filePath = stringValue(configArg.filePath) || stringValue(configArg.file_path) || stringValue(rawObject?.filePath) || stringValue(rawObject?.file_path) || stringValue(rawObject?.path) || stringValue(metadata.filePath) || stringValue(metadata.file_path);
    const bytes = numberValue(rawObject?.bytes ?? rawObject?.size ?? rawObject?.st_size);
    if (bytes === undefined && !filePath) {
      return rawDataArg;
    }

    const name = configArg.name || stringValue(rawObject?.name) || fileName(filePath) || filesizeDefaultName;
    const megabytes = numberValue(rawObject?.file) ?? (bytes !== undefined ? roundMegabytes(bytes) : undefined);
    const lastUpdated = timestampValue(rawObject?.lastUpdated ?? rawObject?.last_updated ?? rawObject?.mtime ?? rawObject?.modified);
    const created = timestampValue(rawObject?.created ?? rawObject?.ctime);
    const uniqueBase = SimpleLocalMapper.slug(filePath || name);
    const entities: ISimpleLocalEntitySnapshot[] = [
      {
        id: 'file',
        uniqueId: `${filesizeProfile.domain}_${uniqueBase}_file`,
        name: 'Size',
        platform: 'sensor',
        state: megabytes ?? null,
        available: megabytes !== undefined,
        writable: false,
        unit: 'MB',
        deviceClass: 'data_size',
        stateClass: 'measurement',
      },
      {
        id: 'bytes',
        uniqueId: `${filesizeProfile.domain}_${uniqueBase}_bytes`,
        name: 'Size in bytes',
        platform: 'sensor',
        state: bytes ?? null,
        available: bytes !== undefined,
        writable: false,
        unit: 'B',
        deviceClass: 'data_size',
        stateClass: 'measurement',
        attributes: {
          entityRegistryEnabledDefault: false,
          entityCategory: 'diagnostic',
        },
      },
      {
        id: 'last_updated',
        uniqueId: `${filesizeProfile.domain}_${uniqueBase}_last_updated`,
        name: 'Last updated',
        platform: 'sensor',
        state: lastUpdated ?? null,
        available: lastUpdated !== undefined,
        writable: false,
        deviceClass: 'timestamp',
        attributes: {
          entityRegistryEnabledDefault: false,
          entityCategory: 'diagnostic',
        },
      },
      {
        id: 'created',
        uniqueId: `${filesizeProfile.domain}_${uniqueBase}_created`,
        name: 'Created',
        platform: 'sensor',
        state: created ?? null,
        available: created !== undefined,
        writable: false,
        deviceClass: 'timestamp',
        attributes: {
          entityRegistryEnabledDefault: false,
          entityCategory: 'diagnostic',
        },
      },
    ];

    return {
      device: {
        id: configArg.uniqueId || filePath || name,
        name,
        manufacturer: filesizeProfile.manufacturer,
        model: filesizeProfile.model,
        protocol: filesizeProfile.defaultProtocol,
        attributes: {
          filePath,
        },
      },
      entities,
      online: configArg.online ?? true,
      updatedAt: new Date().toISOString(),
      source: 'manual',
      rawData: rawDataArg,
    } satisfies ISimpleLocalSnapshot;
  }
}

const roundMegabytes = (bytesArg: number): number => Math.round((bytesArg / 1e6) * 100) / 100;

const timestampValue = (valueArg: unknown): string | undefined => {
  if (valueArg instanceof Date) {
    return valueArg.toISOString();
  }
  return stringValue(valueArg);
};

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

const fileName = (valueArg: string | undefined): string | undefined => valueArg?.split(/[\\/]/).filter(Boolean).pop();

const isRecord = (valueArg: unknown): valueArg is Record<string, unknown> => Boolean(valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg));

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