import * as plugins from '../../plugins.js';
import type { IIntegrationEntity } from '../../core/types.js';
import type { IIppJobInfo, IIppMarkerInfo, IIppSnapshot } from './ipp.types.js';

const ippDomain = 'ipp';

export class IppMapper {
  public static toDevices(snapshotArg: IIppSnapshot): plugins.shxInterfaces.data.IDeviceDefinition[] {
    const updatedAt = snapshotArg.updatedAt || new Date().toISOString();
    const features: plugins.shxInterfaces.data.IDeviceFeature[] = [
      { id: 'status', capability: 'sensor', name: 'Printer status', readable: true, writable: false },
      { id: 'queued_jobs', capability: 'sensor', name: 'Queued jobs', readable: true, writable: false },
      { id: 'accepting_jobs', capability: 'sensor', name: 'Accepting jobs', readable: true, writable: false },
    ];
    const state: plugins.shxInterfaces.data.IDeviceState[] = [
      { featureId: 'status', value: snapshotArg.status.printerState, updatedAt },
      { featureId: 'queued_jobs', value: snapshotArg.status.queuedJobCount ?? null, updatedAt },
      { featureId: 'accepting_jobs', value: snapshotArg.status.acceptingJobs ?? null, updatedAt },
    ];

    for (const marker of snapshotArg.markers) {
      const featureId = `marker_${marker.index}`;
      features.push({ id: featureId, capability: 'sensor', name: marker.name, readable: true, writable: false, unit: '%' });
      state.push({ featureId, value: marker.level ?? null, updatedAt });
    }

    for (const job of snapshotArg.jobs) {
      const featureId = `job_${this.slug(job.id)}`;
      features.push({ id: featureId, capability: 'sensor', name: job.name || `Job ${job.id}`, readable: true, writable: false });
      state.push({ featureId, value: job.state || null, updatedAt });
    }

    return [{
      id: this.printerDeviceId(snapshotArg),
      integrationDomain: ippDomain,
      name: this.printerName(snapshotArg),
      protocol: 'http',
      manufacturer: snapshotArg.printer.manufacturer || 'Unknown',
      model: snapshotArg.printer.model || snapshotArg.printer.makeAndModel || 'IPP printer',
      online: snapshotArg.online,
      features,
      state,
      metadata: this.cleanAttributes({
        serialNumber: snapshotArg.printer.serialNumber,
        uuid: snapshotArg.printer.uuid,
        location: snapshotArg.printer.location,
        info: snapshotArg.printer.info,
        moreInfo: snapshotArg.printer.moreInfo,
        commandSet: snapshotArg.printer.commandSet,
        uriSupported: snapshotArg.printer.uriSupported,
        host: snapshotArg.printer.host,
        port: snapshotArg.printer.port,
        basePath: snapshotArg.printer.basePath,
        tls: snapshotArg.printer.tls,
        source: snapshotArg.source,
        stateReasons: snapshotArg.status.stateReasons,
        markerCount: snapshotArg.markers.length,
        jobCount: snapshotArg.jobs.length,
        error: snapshotArg.error,
      }),
    }];
  }

  public static toEntities(snapshotArg: IIppSnapshot): IIntegrationEntity[] {
    const deviceId = this.printerDeviceId(snapshotArg);
    const uniqueBase = this.uniqueBase(snapshotArg);
    const baseName = this.printerName(snapshotArg);
    const entities: IIntegrationEntity[] = [];

    entities.push(this.entity('sensor', 'printer', `${baseName} Status`, snapshotArg.status.printerState, deviceId, uniqueBase, snapshotArg.online, {
      deviceClass: 'enum',
      options: ['idle', 'printing', 'stopped'],
      info: snapshotArg.printer.info,
      serial: snapshotArg.printer.serialNumber,
      uuid: snapshotArg.printer.uuid,
      location: snapshotArg.printer.location,
      stateMessage: snapshotArg.status.stateMessage,
      stateReason: snapshotArg.status.stateReasons,
      commandSet: snapshotArg.printer.commandSet,
      uriSupported: snapshotArg.printer.uriSupported?.join(','),
      manufacturer: snapshotArg.printer.manufacturer,
      model: snapshotArg.printer.model,
      source: snapshotArg.source,
      error: snapshotArg.error,
    }));

    if (snapshotArg.status.bootedAt) {
      entities.push(this.entity('sensor', 'uptime', `${baseName} Uptime`, snapshotArg.status.bootedAt, deviceId, uniqueBase, snapshotArg.online, {
        deviceClass: 'timestamp',
        entityCategory: 'diagnostic',
      }));
    }

    if (snapshotArg.status.queuedJobCount !== undefined) {
      entities.push(this.entity('sensor', 'queued_jobs', `${baseName} Queued Jobs`, snapshotArg.status.queuedJobCount, deviceId, uniqueBase, snapshotArg.online, {
        stateClass: 'measurement',
      }));
    }

    if (snapshotArg.status.acceptingJobs !== undefined) {
      entities.push(this.entity('binary_sensor', 'accepting_jobs', `${baseName} Accepting Jobs`, snapshotArg.status.acceptingJobs ? 'on' : 'off', deviceId, uniqueBase, snapshotArg.online, {
        deviceClass: 'running',
      }));
    }

    for (const marker of snapshotArg.markers) {
      entities.push(this.markerEntity(marker, deviceId, uniqueBase, snapshotArg.online));
    }

    for (const job of snapshotArg.jobs) {
      entities.push(this.jobEntity(job, deviceId, uniqueBase, baseName, snapshotArg.online));
    }

    return entities;
  }

  public static printerDeviceId(snapshotArg: IIppSnapshot): string {
    return `ipp.printer.${this.uniqueBase(snapshotArg)}`;
  }

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

  private static markerEntity(markerArg: IIppMarkerInfo, deviceIdArg: string, uniqueBaseArg: string, availableArg: boolean): IIntegrationEntity {
    return this.entity('sensor', `marker_${markerArg.index}`, markerArg.name, markerArg.level ?? null, deviceIdArg, uniqueBaseArg, availableArg, {
      unit: '%',
      stateClass: 'measurement',
      markerType: markerArg.type,
      markerKind: markerArg.kind,
      markerColor: markerArg.color,
      markerLowLevel: markerArg.lowLevel,
      markerHighLevel: markerArg.highLevel,
    });
  }

  private static jobEntity(jobArg: IIppJobInfo, deviceIdArg: string, uniqueBaseArg: string, baseNameArg: string, availableArg: boolean): IIntegrationEntity {
    return this.entity('sensor', `job_${this.slug(jobArg.id)}`, jobArg.name || `${baseNameArg} Job ${jobArg.id}`, jobArg.state || 'unknown', deviceIdArg, uniqueBaseArg, availableArg, {
      jobId: jobArg.id,
      owner: jobArg.owner,
      impressionsCompleted: jobArg.impressionsCompleted,
      createdAt: jobArg.createdAt,
      processingAt: jobArg.processingAt,
      completedAt: jobArg.completedAt,
      ...jobArg.attributes,
    });
  }

  private static entity(platformArg: IIntegrationEntity['platform'], keyArg: string, nameArg: string, stateArg: unknown, deviceIdArg: string, uniqueBaseArg: string, availableArg: boolean, attributesArg: Record<string, unknown>): IIntegrationEntity {
    return {
      id: `${platformArg}.${this.slug(nameArg)}`,
      uniqueId: `ipp_${uniqueBaseArg}_${keyArg}`,
      integrationDomain: ippDomain,
      deviceId: deviceIdArg,
      platform: platformArg,
      name: nameArg,
      state: stateArg,
      attributes: this.cleanAttributes(attributesArg),
      available: availableArg,
    };
  }

  private static printerName(snapshotArg: IIppSnapshot): string {
    return snapshotArg.printer.name || snapshotArg.printer.model || snapshotArg.printer.host || 'IPP printer';
  }

  private static uniqueBase(snapshotArg: IIppSnapshot): string {
    return this.slug(snapshotArg.printer.uuid || snapshotArg.printer.serialNumber || snapshotArg.printer.id || snapshotArg.printer.host || this.printerName(snapshotArg));
  }

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