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 { ILocalCalendarConfig, ILocalCalendarEvent } from './local_calendar.types.js';
import { localCalendarDefaultName, localCalendarProfile } from './local_calendar.types.js';

export class LocalCalendarMapper {
  public static toSnapshot(optionsArg: Omit<ISimpleLocalSnapshotOptions<ILocalCalendarConfig>, 'profile'>): ISimpleLocalSnapshot {
    return SimpleLocalMapper.toSnapshot({
      ...optionsArg,
      profile: localCalendarProfile,
      rawData: this.normalizeRawData(optionsArg.config, optionsArg.rawData, optionsArg.source),
    });
  }

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

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

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

  private static normalizeRawData(configArg: ILocalCalendarConfig, rawDataArg: unknown, sourceArg?: ISimpleLocalSnapshotOptions<ILocalCalendarConfig>['source']): unknown {
    if (isRecord(rawDataArg) && 'device' in rawDataArg && 'entities' in rawDataArg) {
      return rawDataArg;
    }

    const rawObject = isRecord(rawDataArg) ? rawDataArg : undefined;
    if (!rawObject) {
      return rawDataArg;
    }

    const metadata = configArg.metadata || {};
    const filePath = stringValue(configArg.filePath) || stringValue(configArg.file_path) || stringValue(configArg.icsFile) || stringValue(configArg.ics_file) || stringValue(rawObject.filePath) || stringValue(rawObject.ics_file) || stringValue(metadata.filePath) || stringValue(metadata.ics_file);
    const events = eventArray(rawObject.events);
    const eventCount = numberValue(rawObject.eventCount) ?? events.length;
    const next = eventRecord(rawObject.nextEvent) || events[0];
    if (!filePath && !events.length && rawObject.eventCount === undefined) {
      return rawDataArg;
    }

    const name = configArg.name || stringValue(configArg.calendarName) || stringValue(configArg.calendar_name) || stringValue(rawObject.calendarName) || localCalendarDefaultName;
    const uniqueBase = SimpleLocalMapper.slug(filePath || name);
    const entities: ISimpleLocalEntitySnapshot[] = [
      {
        id: 'next_event',
        uniqueId: `${localCalendarProfile.domain}_${uniqueBase}_next_event`,
        name: 'Next event',
        platform: 'sensor',
        state: next?.summary ?? null,
        available: Boolean(next) || eventCount === 0,
        writable: false,
        attributes: {
          uid: next?.uid,
          start: next?.start,
          end: next?.end,
          description: next?.description,
          location: next?.location,
          rrule: next?.rrule,
          allDay: next?.allDay,
          filePath,
        },
      },
      {
        id: 'event_count',
        uniqueId: `${localCalendarProfile.domain}_${uniqueBase}_event_count`,
        name: 'Event count',
        platform: 'sensor',
        state: eventCount,
        available: true,
        writable: false,
        attributes: {
          filePath,
          contentBytes: numberValue(rawObject.contentBytes),
        },
      },
    ];

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

const eventRecord = (valueArg: unknown): ILocalCalendarEvent | undefined => isRecord(valueArg) ? valueArg as ILocalCalendarEvent : undefined;

const eventArray = (valueArg: unknown): ILocalCalendarEvent[] => Array.isArray(valueArg) ? valueArg.filter((eventArg): eventArg is ILocalCalendarEvent => isRecord(eventArg)) : [];

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;
