import type { IConfigFlow, IConfigFlowContext, IConfigFlowStep, IDiscoveryCandidate } from '../../core/types.js';
import type { IDirectvConfig, IDirectvSnapshot } from './directv.types.js';
import { directvDefaultPort } from './directv.types.js';

export class DirectvConfigFlow implements IConfigFlow<IDirectvConfig> {
  public async start(candidateArg: IDiscoveryCandidate, contextArg: IConfigFlowContext): Promise<IConfigFlowStep<IDirectvConfig>> {
    void contextArg;
    if ((candidateArg.source === 'ssdp' || candidateArg.source === 'manual') && (candidateArg.host || this.snapshotMetadata(candidateArg))) {
      return {
        kind: 'done',
        title: candidateArg.name || candidateArg.host || 'DirecTV configured',
        config: this.configFromCandidate(candidateArg),
      };
    }

    return {
      kind: 'form',
      title: 'Connect DirecTV receiver',
      description: 'Configure the local DirecTV SHEF HTTP endpoint. Live state and commands require a receiver host or injected client/executor.',
      fields: [
        { name: 'host', label: candidateArg.host ? `Host (${candidateArg.host})` : 'Host', type: 'text', required: true },
        { name: 'port', label: `Port (${candidateArg.port || directvDefaultPort})`, type: 'number' },
        { name: 'name', label: 'Name', type: 'text' },
        { name: 'receiverId', label: 'Receiver ID', type: 'text' },
      ],
      submit: async (valuesArg) => this.submit(candidateArg, valuesArg),
    };
  }

  private async submit(candidateArg: IDiscoveryCandidate, valuesArg: Record<string, unknown>): Promise<IConfigFlowStep<IDirectvConfig>> {
    const host = this.stringValue(valuesArg.host) || candidateArg.host;
    const snapshot = this.snapshotMetadata(candidateArg);
    const hasInjectedRuntime = Boolean(candidateArg.metadata?.client || candidateArg.metadata?.commandExecutor || snapshot);
    if (!host && !hasInjectedRuntime) {
      return { kind: 'error', title: 'DirecTV setup failed', error: 'DirecTV setup requires a host, snapshot, injected client, or command executor.' };
    }

    const port = this.numberValue(valuesArg.port) || candidateArg.port || directvDefaultPort;
    if (!Number.isInteger(port) || port < 1 || port > 65535) {
      return { kind: 'error', title: 'DirecTV setup failed', error: 'DirecTV port must be between 1 and 65535.' };
    }

    return {
      kind: 'done',
      title: this.stringValue(valuesArg.name) || candidateArg.name || host || 'DirecTV configured',
      config: {
        ...this.configFromCandidate(candidateArg),
        host,
        port,
        name: this.stringValue(valuesArg.name) || candidateArg.name,
        receiverId: this.receiverId(this.stringValue(valuesArg.receiverId) || this.stringMetadata(candidateArg, 'receiverId') || candidateArg.id || snapshot?.device.receiverId),
      },
    };
  }

  private configFromCandidate(candidateArg: IDiscoveryCandidate): IDirectvConfig {
    const snapshot = this.snapshotMetadata(candidateArg);
    const receiverId = this.receiverId(this.stringMetadata(candidateArg, 'receiverId') || candidateArg.id || candidateArg.serialNumber || snapshot?.device.receiverId);
    return {
      host: candidateArg.host || snapshot?.device.host,
      port: candidateArg.port || snapshot?.device.port || directvDefaultPort,
      name: candidateArg.name || snapshot?.device.name,
      receiverId,
      uniqueId: receiverId || candidateArg.id,
      timeoutMs: 8000,
      snapshot,
      client: candidateArg.metadata?.client as IDirectvConfig['client'],
      commandExecutor: candidateArg.metadata?.commandExecutor as IDirectvConfig['commandExecutor'],
    };
  }

  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 stringMetadata(candidateArg: IDiscoveryCandidate, keyArg: string): string | undefined {
    const value = candidateArg.metadata?.[keyArg];
    return this.stringValue(value);
  }

  private snapshotMetadata(candidateArg: IDiscoveryCandidate): IDirectvSnapshot | undefined {
    const value = candidateArg.metadata?.snapshot;
    return value && typeof value === 'object' && !Array.isArray(value) ? value as IDirectvSnapshot : undefined;
  }

  private receiverId(valueArg: string | undefined): string | undefined {
    return valueArg?.replace(/^RID-/i, '').replace(/\s+/g, '') || undefined;
  }
}
