import type * as shxInterfaces from '@smarthome.exchange/interfaces';
import { SimpleLocalMapper, type IIntegrationEntity, type ISimpleLocalSnapshot, type ISimpleLocalSnapshotOptions, type TSimpleLocalRawData } from '../../core/index.js';
import type { IFolderWatcherConfig } from './folder_watcher.types.js';
import { folderWatcherDefaultName, folderWatcherDefaultPattern, folderWatcherProfile } from './folder_watcher.types.js';

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

  public static toSnapshotFromRaw(configArg: IFolderWatcherConfig, 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(folderWatcherProfile, snapshotArg);
  }

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

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

  private static normalizeRawData(configArg: IFolderWatcherConfig, rawDataArg: unknown): unknown {
    if (isRecord(rawDataArg) && 'device' in rawDataArg && 'entities' in rawDataArg) {
      return rawDataArg;
    }
    const rawObject = isRecord(rawDataArg) ? rawDataArg : undefined;
    if (!rawObject || Array.isArray(rawObject.entities)) {
      return rawDataArg;
    }

    const metadata = configArg.metadata || {};
    const folderPath = stringValue(configArg.folder) || stringValue(configArg.folderPath) || stringValue(configArg.folder_path) || stringValue(configArg.path) || stringValue(rawObject.folder) || stringValue(rawObject.folderPath) || stringValue(rawObject.folder_path) || stringValue(metadata.folder) || stringValue(metadata.folderPath) || stringValue(metadata.folder_path);
    const patterns = stringArray(configArg.patterns) || stringArray(rawObject.patterns) || stringArray(metadata.patterns) || [folderWatcherDefaultPattern];
    const latestEvent = eventRecord(rawObject.last_event) || eventRecord(rawObject.latestEvent) || lastEvent(rawObject.events);
    const watchedFiles = numberValue(rawObject.watched_files ?? rawObject.watchedFiles) ?? stringArray(rawObject.file_list)?.length ?? 0;
    const name = configArg.name || stringValue(rawObject.name) || (folderPath ? `Folder Watcher ${folderPath}` : folderWatcherDefaultName);
    const uniqueBase = SimpleLocalMapper.slug(folderPath || name);

    return {
      device: {
        id: configArg.uniqueId || folderPath || name,
        name,
        manufacturer: folderWatcherProfile.manufacturer,
        model: folderWatcherProfile.model,
        protocol: folderWatcherProfile.defaultProtocol,
        attributes: {
          folder: folderPath,
          patterns,
        },
      },
      entities: [{
        id: 'folder_watcher',
        uniqueId: `${folderWatcherProfile.domain}_${uniqueBase}_event`,
        name: 'Folder Watcher Event',
        platform: 'sensor',
        state: stringValue(latestEvent?.event_type) || stringValue(latestEvent?.eventType) || 'idle',
        available: Boolean(folderPath) || rawDataArg !== undefined,
        writable: false,
        attributes: {
          haPlatform: 'event',
          eventTypes: ['closed', 'created', 'deleted', 'modified', 'moved'],
          folder: folderPath,
          patterns,
          watched_files: watchedFiles,
          event_type: latestEvent?.event_type ?? latestEvent?.eventType,
          path: latestEvent?.path,
          file: latestEvent?.file,
          dest_path: latestEvent?.dest_path ?? latestEvent?.destPath,
          dest_file: latestEvent?.dest_file ?? latestEvent?.destFile,
          dest_folder: latestEvent?.dest_folder ?? latestEvent?.destFolder,
        },
      }],
      online: configArg.online ?? true,
      updatedAt: new Date().toISOString(),
      source: 'manual',
      rawData: rawDataArg,
    } satisfies ISimpleLocalSnapshot;
  }
}

const lastEvent = (valueArg: unknown): Record<string, unknown> | undefined => Array.isArray(valueArg) ? valueArg.filter(isRecord).at(-1) : undefined;

const eventRecord = (valueArg: unknown): Record<string, unknown> | undefined => isRecord(valueArg) ? valueArg : undefined;

const stringArray = (valueArg: unknown): string[] | undefined => Array.isArray(valueArg) ? valueArg.filter((itemArg): itemArg is string => typeof itemArg === 'string' && itemArg.trim().length > 0) : undefined;

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 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;
