import type { IConfigFlow, IConfigFlowContext, IConfigFlowStep, IDiscoveryCandidate } from '../../core/types.js';
import { broadlinkDefaultPort, broadlinkDefaultTimeoutSeconds } from './broadlink.constants.js';
import type { IBroadlinkConfig, IBroadlinkSnapshot, TBroadlinkDeviceType } from './broadlink.types.js';

export class BroadlinkConfigFlow implements IConfigFlow<IBroadlinkConfig> {
  public async start(candidateArg: IDiscoveryCandidate, contextArg: IConfigFlowContext): Promise<IConfigFlowStep<IBroadlinkConfig>> {
    void contextArg;
    const metadata = candidateArg.metadata || {};
    const host = candidateArg.host || this.stringValue(metadata.host) || '';
    const deviceType = candidateArg.model || this.stringValue(metadata.deviceType) || '';
    const name = candidateArg.name || this.stringValue(metadata.name) || '';

    return {
      kind: 'form',
      title: 'Connect Broadlink device',
      description: 'Provide the local host and device identity. This TypeScript port maps snapshots and commands, and requires an injected executor for live Broadlink UDP/auth traffic.',
      fields: [
        { name: 'host', label: host ? `Host (${host})` : 'Host', type: 'text', required: true },
        { name: 'port', label: `Port (${candidateArg.port || broadlinkDefaultPort})`, type: 'number' },
        { name: 'timeout', label: `Timeout seconds (${broadlinkDefaultTimeoutSeconds})`, type: 'number' },
        { name: 'name', label: name ? `Name (${name})` : 'Name', type: 'text' },
        { name: 'type', label: deviceType ? `Device type (${deviceType})` : 'Device type', type: 'text' },
        { name: 'macAddress', label: candidateArg.macAddress ? `MAC (${candidateArg.macAddress})` : 'MAC address', type: 'text' },
        { name: 'snapshotJson', label: 'Snapshot JSON', type: 'text' },
      ],
      submit: async (valuesArg) => this.submit(candidateArg, valuesArg),
    };
  }

  private async submit(candidateArg: IDiscoveryCandidate, valuesArg: Record<string, unknown>): Promise<IConfigFlowStep<IBroadlinkConfig>> {
    const snapshot = this.snapshotFromInput(valuesArg.snapshotJson);
    if (snapshot instanceof Error) {
      return { kind: 'error', title: 'Invalid snapshot', error: snapshot.message };
    }

    const host = this.stringValue(valuesArg.host) || candidateArg.host || snapshot?.host;
    if (!host && !snapshot) {
      return { kind: 'error', title: 'Host required', error: 'Broadlink setup requires a host unless a config is created directly from a snapshot.' };
    }

    const metadata = candidateArg.metadata || {};
    const config: IBroadlinkConfig = {
      host,
      port: this.numberValue(valuesArg.port) || candidateArg.port || snapshot?.port || broadlinkDefaultPort,
      timeout: this.numberValue(valuesArg.timeout) || this.numberValue(metadata.timeout) || broadlinkDefaultTimeoutSeconds,
      name: this.stringValue(valuesArg.name) || candidateArg.name,
      macAddress: this.stringValue(valuesArg.macAddress) || candidateArg.macAddress,
      deviceId: candidateArg.id,
      type: (this.stringValue(valuesArg.type) || this.stringValue(metadata.deviceType) || candidateArg.model) as TBroadlinkDeviceType | undefined,
      devtype: this.numberValue(metadata.devtype),
      model: candidateArg.model,
      manufacturer: candidateArg.manufacturer || 'Broadlink',
      isLocked: metadata.isLocked === true,
      snapshot,
      metadata: {
        discoverySource: candidateArg.source,
        discoveryMetadata: metadata,
        liveUdpAuthImplemented: false,
      },
    };

    return {
      kind: 'done',
      title: 'Broadlink device configured',
      config,
    };
  }

  private snapshotFromInput(valueArg: unknown): IBroadlinkSnapshot | undefined | Error {
    const text = this.stringValue(valueArg);
    if (!text) {
      return undefined;
    }
    try {
      const parsed = JSON.parse(text) as IBroadlinkSnapshot;
      if (!parsed || !Array.isArray(parsed.devices)) {
        return new Error('Snapshot JSON must include a devices array.');
      }
      return parsed;
    } catch (errorArg) {
      return errorArg instanceof Error ? errorArg : new Error(String(errorArg));
    }
  }

  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)) {
      return valueArg;
    }
    if (typeof valueArg === 'string' && valueArg.trim()) {
      const value = Number(valueArg);
      return Number.isFinite(value) ? value : undefined;
    }
    return undefined;
  }
}
