import type { IConfigFlow, IConfigFlowContext, IConfigFlowStep, IDiscoveryCandidate } from '../../core/types.js';
import type { ITplinkConfig, ITplinkCredentials, ITplinkSnapshot } from './tplink.types.js';
import { tplinkDefaultHttpPort } from './tplink.types.js';

export class TplinkConfigFlow implements IConfigFlow<ITplinkConfig> {
  public async start(candidateArg: IDiscoveryCandidate, contextArg: IConfigFlowContext): Promise<IConfigFlowStep<ITplinkConfig>> {
    void contextArg;
    const metadata = candidateArg.metadata || {};
    const host = candidateArg.host || this.stringValue(metadata.host) || '';
    const model = candidateArg.model || this.stringValue(metadata.model) || '';
    const alias = candidateArg.name || this.stringValue(metadata.alias) || this.stringValue(metadata.name) || '';
    const requiresAuth = metadata.requiresAuth === true || metadata.encryptionType !== undefined || metadata.connectionParameters !== undefined;

    return {
      kind: 'form',
      title: 'Connect TP-Link Smart Home device',
      description: requiresAuth
        ? 'Provide the device host and TP-Link cloud credentials used by Kasa/Tapo devices. A snapshot can be supplied for read-only setup.'
        : 'Provide the device host. Credentials are optional for legacy Kasa devices and required by many newer Kasa/Tapo devices. A snapshot can be supplied for read-only setup.',
      fields: [
        { name: 'host', label: host ? `Host (${host})` : 'Host', type: 'text', required: true },
        { name: 'port', label: `Port (${candidateArg.port || tplinkDefaultHttpPort})`, type: 'number' },
        { name: 'username', label: 'TP-Link username', type: 'text', required: requiresAuth },
        { name: 'password', label: 'TP-Link password', type: 'password', required: requiresAuth },
        { name: 'alias', label: alias ? `Alias (${alias})` : 'Alias', type: 'text' },
        { name: 'model', label: model ? `Model (${model})` : 'Model', 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<ITplinkConfig>> {
    const host = this.stringValue(valuesArg.host) || candidateArg.host;
    if (!host) {
      return { kind: 'error', title: 'Host required', error: 'TP-Link setup requires a host unless a config is created directly from a snapshot.' };
    }

    const snapshot = this.snapshotFromInput(valuesArg.snapshotJson);
    if (snapshot instanceof Error) {
      return { kind: 'error', title: 'Invalid snapshot', error: snapshot.message };
    }

    const username = this.stringValue(valuesArg.username);
    const password = this.stringValue(valuesArg.password);
    const credentials: ITplinkCredentials | undefined = username || password ? { username, password } : undefined;
    const config: ITplinkConfig = {
      host,
      port: this.numberValue(valuesArg.port) || candidateArg.port || tplinkDefaultHttpPort,
      alias: this.stringValue(valuesArg.alias) || candidateArg.name,
      model: this.stringValue(valuesArg.model) || candidateArg.model,
      macAddress: candidateArg.macAddress,
      deviceId: candidateArg.id,
      credentials,
      snapshot,
      connectionParameters: this.record(candidateArg.metadata?.connectionParameters)
        ? candidateArg.metadata.connectionParameters
        : undefined,
      usesHttp: candidateArg.metadata?.usesHttp === true ? true : undefined,
      metadata: {
        discoverySource: candidateArg.source,
        discoveryMetadata: candidateArg.metadata,
        liveLocalWritesImplemented: false,
      },
    };

    return {
      kind: 'done',
      title: 'TP-Link Smart Home device configured',
      config,
    };
  }

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

  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;
  }

  private record(valueArg: unknown): valueArg is Record<string, unknown> {
    return typeof valueArg === 'object' && valueArg !== null && !Array.isArray(valueArg);
  }
}
