import type { IConfigFlow, IConfigFlowContext, IConfigFlowStep, IDiscoveryCandidate } from '../../core/types.js';
import type { IElgatoConfig, IElgatoRawData, IElgatoSnapshot } from './elgato.types.js';
import { elgatoDefaultPort, elgatoDefaultTimeoutMs } from './elgato.types.js';

export class ElgatoConfigFlow implements IConfigFlow<IElgatoConfig> {
  public async start(candidateArg: IDiscoveryCandidate, contextArg: IConfigFlowContext): Promise<IConfigFlowStep<IElgatoConfig>> {
    void contextArg;
    return {
      kind: 'form',
      title: 'Configure Elgato Light',
      description: 'Configure a local Elgato Key Light endpoint, or use snapshot/manual data from the discovery candidate.',
      fields: [
        { name: 'host', label: 'Host', type: 'text' },
        { name: 'port', label: 'Port', type: 'number' },
        { name: 'name', label: 'Name', type: 'text' },
        { name: 'serialNumber', label: 'Serial number', type: 'text' },
        { name: 'macAddress', label: 'MAC address', type: 'text' },
      ],
      submit: async (valuesArg) => this.submit(candidateArg, valuesArg),
    };
  }

  private async submit(candidateArg: IDiscoveryCandidate, valuesArg: Record<string, unknown>): Promise<IConfigFlowStep<IElgatoConfig>> {
    const metadata = candidateArg.metadata || {};
    const parsed = parseEndpoint(this.stringValue(valuesArg.host) || this.stringMetadata(metadata, 'url'));
    const snapshot = snapshotValue(metadata.snapshot);
    const rawData = rawDataValue(metadata.rawData);
    const host = parsed?.host || this.stringValue(valuesArg.host) || candidateArg.host || snapshot?.device.host;
    const port = this.numberValue(valuesArg.port) || candidateArg.port || parsed?.port || snapshot?.device.port || elgatoDefaultPort;
    const hasManualData = Boolean(snapshot || rawData || metadata.client);

    if (!host && !hasManualData) {
      return { kind: 'error', title: 'Elgato setup failed', error: 'Elgato host, injected client, snapshot, or manual data is required.' };
    }
    if (!this.validPort(port)) {
      return { kind: 'error', title: 'Elgato setup failed', error: 'Elgato port must be between 1 and 65535.' };
    }

    const serialNumber = this.stringValue(valuesArg.serialNumber) || candidateArg.serialNumber || snapshot?.device.serialNumber || this.stringMetadata(metadata, 'serialNumber');
    const macAddress = this.stringValue(valuesArg.macAddress) || candidateArg.macAddress || snapshot?.device.macAddress || this.stringMetadata(metadata, 'macAddress');
    const config: IElgatoConfig = {
      host,
      port,
      timeoutMs: elgatoDefaultTimeoutMs,
      name: this.stringValue(valuesArg.name) || candidateArg.name || snapshot?.device.name || this.stringMetadata(metadata, 'name'),
      manufacturer: candidateArg.manufacturer || snapshot?.device.manufacturer,
      model: candidateArg.model || snapshot?.device.model,
      uniqueId: candidateArg.id || snapshot?.device.id || serialNumber || macAddress || (host ? `${host}:${port}` : undefined),
      serialNumber,
      macAddress,
      snapshot,
      rawData,
      client: metadata.client as IElgatoConfig['client'],
      commandExecutor: metadata.commandExecutor as IElgatoConfig['commandExecutor'],
    };

    return { kind: 'done', title: 'Elgato Light configured', config };
  }

  private stringValue(valueArg: unknown): string | undefined {
    return typeof valueArg === 'string' && valueArg.trim() ? valueArg.trim().replace(/\/$/, '') : 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(metadataArg: Record<string, unknown>, keyArg: string): string | undefined {
    return this.stringValue(metadataArg[keyArg]);
  }

  private validPort(valueArg: number): boolean {
    return Number.isInteger(valueArg) && valueArg > 0 && valueArg <= 65535;
  }
}

const parseEndpoint = (valueArg: string | undefined): { host: string; port?: number } | undefined => {
  if (!valueArg || !/^[a-z][a-z0-9+.-]*:\/\//i.test(valueArg)) {
    return undefined;
  }
  try {
    const url = new URL(valueArg);
    return { host: url.hostname, port: url.port ? Number(url.port) : undefined };
  } catch {
    return undefined;
  }
};

const snapshotValue = (valueArg: unknown): IElgatoSnapshot | undefined => {
  return valueArg && typeof valueArg === 'object' && 'device' in valueArg && 'light' in valueArg ? valueArg as IElgatoSnapshot : undefined;
};

const rawDataValue = (valueArg: unknown): Partial<IElgatoRawData> | undefined => {
  return valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg) ? valueArg as Partial<IElgatoRawData> : undefined;
};
