import type { IConfigFlow, IConfigFlowContext, IConfigFlowStep, IDiscoveryCandidate } from '../../core/types.js';
import { dlinkDefaultName, dlinkDefaultPort, dlinkDefaultTimeoutMs, dlinkDefaultUsername } from './dlink.constants.js';
import type { IDlinkConfig, IDlinkSnapshot } from './dlink.types.js';

export class DlinkConfigFlow implements IConfigFlow<IDlinkConfig> {
  public async start(candidateArg: IDiscoveryCandidate, contextArg: IConfigFlowContext): Promise<IConfigFlowStep<IDlinkConfig>> {
    void contextArg;
    const metadata = candidateArg.metadata || {};
    const snapshot = metadata.snapshot as IDlinkSnapshot | undefined;
    const host = candidateArg.host || this.stringValue(metadata.host) || snapshot?.device.host || '';
    const username = this.stringValue(metadata.username) || dlinkDefaultUsername;
    const useLegacyProtocol = this.booleanValue(metadata.useLegacyProtocol ?? metadata.use_legacy_protocol) ?? false;

    return {
      kind: 'form',
      title: candidateArg.source === 'dhcp' ? 'Confirm D-Link smart plug' : 'Connect D-Link smart plug',
      description: 'Provide local D-Link smart plug credentials. Native HNAP HTTP is used for live reads and switch commands unless an injected client/executor is configured.',
      fields: [
        { name: 'host', label: host ? `Host (${host})` : 'Host', type: 'text', required: !snapshot },
        { name: 'username', label: `Username (${username})`, type: 'text' },
        { name: 'password', label: 'Password', type: 'password', required: !snapshot },
        { name: 'useLegacyProtocol', label: `Use legacy protocol (${useLegacyProtocol ? 'yes' : 'no'})`, type: 'boolean' },
        { name: 'nativeHttpEnabled', label: 'Enable native local HNAP HTTP', type: 'boolean' },
        { name: 'name', label: candidateArg.name ? `Name (${candidateArg.name})` : 'Name', type: 'text' },
        { name: 'port', label: `Port (${candidateArg.port || snapshot?.device.port || dlinkDefaultPort})`, type: 'number' },
        { name: 'timeoutMs', label: `Timeout ms (${dlinkDefaultTimeoutMs})`, type: 'number' },
        { 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<IDlinkConfig>> {
    const metadata = candidateArg.metadata || {};
    const candidateSnapshot = metadata.snapshot as IDlinkSnapshot | undefined;
    const snapshot = this.snapshotFromInput(valuesArg.snapshotJson) || candidateSnapshot;
    if (snapshot instanceof Error) {
      return { kind: 'error', title: 'Invalid D-Link snapshot', error: snapshot.message };
    }

    const host = this.stringValue(valuesArg.host) || candidateArg.host || this.stringValue(metadata.host) || snapshot?.device.host;
    if (!host && !snapshot) {
      return { kind: 'error', title: 'D-Link setup failed', error: 'D-Link setup requires a host unless a snapshot is provided.' };
    }

    const password = this.stringValue(valuesArg.password) || this.stringValue(metadata.password);
    if (!password && !snapshot) {
      return { kind: 'error', title: 'D-Link setup failed', error: 'D-Link setup requires a password unless a snapshot is provided.' };
    }

    const port = this.numberValue(valuesArg.port) ?? candidateArg.port ?? snapshot?.device.port ?? dlinkDefaultPort;
    if (port < 1 || port > 65535) {
      return { kind: 'error', title: 'D-Link setup failed', error: 'Port must be between 1 and 65535.' };
    }

    const timeoutMs = this.numberValue(valuesArg.timeoutMs) ?? dlinkDefaultTimeoutMs;
    if (timeoutMs < 1) {
      return { kind: 'error', title: 'D-Link setup failed', error: 'Timeout must be greater than zero.' };
    }

    const config: IDlinkConfig = {
      id: candidateArg.id || candidateArg.macAddress || snapshot?.device.id || host,
      host,
      port,
      name: this.stringValue(valuesArg.name) || candidateArg.name || snapshot?.device.name || dlinkDefaultName,
      username: this.stringValue(valuesArg.username) || this.stringValue(metadata.username) || dlinkDefaultUsername,
      password,
      useLegacyProtocol: this.booleanValue(valuesArg.useLegacyProtocol) ?? this.booleanValue(metadata.useLegacyProtocol ?? metadata.use_legacy_protocol) ?? snapshot?.device.useLegacyProtocol ?? false,
      nativeHttpEnabled: this.booleanValue(valuesArg.nativeHttpEnabled) ?? true,
      timeoutMs,
      manufacturer: candidateArg.manufacturer || snapshot?.device.manufacturer,
      model: candidateArg.model || snapshot?.device.modelName,
      macAddress: candidateArg.macAddress || snapshot?.device.macAddress,
      snapshot,
      metadata: {
        discoverySource: candidateArg.source,
        discoveryMetadata: metadata,
        nativeHttpImplemented: true,
        configFlowConnectionVerified: false,
      },
    };

    return {
      kind: 'done',
      title: 'D-Link smart plug configured',
      config,
    };
  }

  private snapshotFromInput(valueArg: unknown): IDlinkSnapshot | undefined | Error {
    if (valueArg && typeof valueArg === 'object') {
      const snapshot = valueArg as IDlinkSnapshot;
      return this.validateSnapshot(snapshot);
    }
    const text = this.stringValue(valueArg);
    if (!text) {
      return undefined;
    }
    try {
      const parsed = JSON.parse(text) as IDlinkSnapshot;
      return this.validateSnapshot(parsed);
    } catch (errorArg) {
      return errorArg instanceof Error ? errorArg : new Error(String(errorArg));
    }
  }

  private validateSnapshot(snapshotArg: IDlinkSnapshot): IDlinkSnapshot | Error {
    if (!snapshotArg || typeof snapshotArg !== 'object' || !snapshotArg.device || !snapshotArg.switch) {
      return new Error('Snapshot JSON must include device and switch objects.');
    }
    return snapshotArg;
  }

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

  private booleanValue(valueArg: unknown): boolean | undefined {
    if (typeof valueArg === 'boolean') {
      return valueArg;
    }
    if (typeof valueArg === 'string') {
      const normalized = valueArg.trim().toLowerCase();
      if (normalized === 'true' || normalized === 'yes' || normalized === '1') {
        return true;
      }
      if (normalized === 'false' || normalized === 'no' || normalized === '0') {
        return false;
      }
    }
    return undefined;
  }
}
