import type { IConfigFlow, IConfigFlowContext, IConfigFlowStep, IDiscoveryCandidate } from '../../core/types.js';
import { brotherDefaultCommunity, brotherDefaultPort, brotherDefaultTimeoutMs, brotherPrinterTypes } from './brother.constants.js';
import type { IBrotherConfig, IBrotherRawData, IBrotherSensors, IBrotherSnapshot, TBrotherPrinterType } from './brother.types.js';

export class BrotherConfigFlow implements IConfigFlow<IBrotherConfig> {
  public async start(candidateArg: IDiscoveryCandidate, contextArg: IConfigFlowContext): Promise<IConfigFlowStep<IBrotherConfig>> {
    void contextArg;
    return {
      kind: 'form',
      title: 'Connect Brother printer',
      description: 'Configure Brother printer polling data. Live SNMP transport requires an injected client; snapshots, raw SNMP data, and manual sensor values are supported locally.',
      fields: [
        { name: 'host', label: 'Host', type: 'text' },
        { name: 'printerType', label: 'Printer type', type: 'select', required: true, options: brotherPrinterTypes.map((typeArg) => ({ label: typeArg, value: typeArg })) },
        { name: 'port', label: 'SNMP port', type: 'number' },
        { name: 'community', label: 'SNMP community', type: 'text' },
        { name: 'name', label: 'Name', type: 'text' },
        { name: 'model', label: 'Model', type: 'text' },
        { name: 'serial', label: 'Serial number', type: 'text' },
      ],
      submit: async (valuesArg) => {
        const metadata = candidateArg.metadata || {};
        const snapshot = snapshotFromMetadata(metadata);
        const rawData = rawDataFromMetadata(metadata);
        const sensors = sensorsFromMetadata(metadata);
        const host = this.stringValue(valuesArg.host) || candidateArg.host || snapshot?.printer.host;
        const hasManualSource = Boolean(snapshot || rawData || sensors || metadata.client);
        if (!host && !hasManualSource) {
          return { kind: 'error', title: 'Brother setup failed', error: 'Brother setup requires a host, snapshot, sensors, rawData, or injected client.' };
        }
        const printerType = this.printerTypeValue(valuesArg.printerType) || this.printerTypeValue(metadata.printerType) || this.printerTypeValue(metadata.type) || snapshot?.printer.printerType || 'laser';
        const port = this.numberValue(valuesArg.port) || candidateArg.port || this.numberValue(metadata.port) || snapshot?.printer.port || brotherDefaultPort;
        const community = this.stringValue(valuesArg.community) || this.stringValue(metadata.community) || brotherDefaultCommunity;
        const serial = this.stringValue(valuesArg.serial) || candidateArg.serialNumber || snapshot?.printer.serialNumber || snapshot?.printer.serial || this.stringValue(metadata.serial) || this.stringValue(metadata.serialNumber);
        const macAddress = candidateArg.macAddress || snapshot?.printer.macAddress || this.stringValue(metadata.macAddress);
        const model = this.stringValue(valuesArg.model) || candidateArg.model || snapshot?.printer.model || this.stringValue(metadata.model);
        const name = this.stringValue(valuesArg.name) || candidateArg.name || snapshot?.printer.name;
        return {
          kind: 'done',
          title: 'Brother printer configured',
          config: {
            host,
            port,
            community,
            printerType,
            name,
            manufacturer: candidateArg.manufacturer || snapshot?.printer.manufacturer || 'Brother',
            model,
            serial,
            serialNumber: serial,
            macAddress,
            firmware: snapshot?.printer.firmware || this.stringValue(metadata.firmware),
            uniqueId: candidateArg.id || serial || macAddress || (host ? `${host}:${port}` : undefined),
            timeoutMs: brotherDefaultTimeoutMs,
            snapshot,
            rawData,
            sensors,
            client: metadata.client as IBrotherConfig['client'],
          },
        };
      },
    };
  }

  private printerTypeValue(valueArg: unknown): TBrotherPrinterType | undefined {
    const value = this.stringValue(valueArg)?.toLowerCase();
    return value === 'ink' || value === 'laser' ? value : undefined;
  }

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

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

const snapshotFromMetadata = (metadataArg: Record<string, unknown>): IBrotherSnapshot | undefined => {
  const snapshot = metadataArg.snapshot;
  return snapshot && typeof snapshot === 'object' && 'printer' in snapshot && 'sensors' in snapshot ? snapshot as IBrotherSnapshot : undefined;
};

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

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