import type { IConfigFlow, IConfigFlowContext, IConfigFlowStep, IDiscoveryCandidate } from '../../core/types.js';
import type { IHunterDouglasPowerViewConfig, IHunterDouglasPowerViewRawData, IHunterDouglasPowerViewSnapshot } from './hunterdouglas_powerview.types.js';
import { hunterDouglasPowerViewDefaultPort, hunterDouglasPowerViewDefaultTimeoutMs } from './hunterdouglas_powerview.types.js';

export class HunterDouglasPowerViewConfigFlow implements IConfigFlow<IHunterDouglasPowerViewConfig> {
  public async start(candidateArg: IDiscoveryCandidate, contextArg: IConfigFlowContext): Promise<IConfigFlowStep<IHunterDouglasPowerViewConfig>> {
    void contextArg;
    return {
      kind: 'form',
      title: 'Configure Hunter Douglas PowerView',
      description: 'Configure a local PowerView Hub endpoint. A host, injected client, snapshot, or raw hub data is required; static snapshots/manual data are read-only unless a command executor is supplied.',
      fields: [
        { name: 'host', label: 'Host', type: 'text' },
        { name: 'port', label: 'Port', type: 'number' },
        { name: 'apiVersion', label: 'API version', type: 'number' },
        { name: 'name', label: 'Name', type: 'text' },
      ],
      submit: async (valuesArg) => this.submit(candidateArg, valuesArg),
    };
  }

  private async submit(candidateArg: IDiscoveryCandidate, valuesArg: Record<string, unknown>): Promise<IConfigFlowStep<IHunterDouglasPowerViewConfig>> {
    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?.hub.host;
    const port = this.numberValue(valuesArg.port) || parsed?.port || candidateArg.port || snapshot?.hub.port || hunterDouglasPowerViewDefaultPort;
    const apiVersion = this.numberValue(valuesArg.apiVersion) || this.numberMetadata(metadata, 'apiVersion') || snapshot?.hub.apiVersion;
    const hasManualData = Boolean(snapshot || rawData || metadata.client);

    if (!host && !hasManualData) {
      return { kind: 'error', title: 'PowerView setup failed', error: 'PowerView host, injected client, snapshot, or manual raw data is required.' };
    }
    if (!this.validPort(port)) {
      return { kind: 'error', title: 'PowerView setup failed', error: 'PowerView port must be between 1 and 65535.' };
    }
    if (apiVersion !== undefined && ![1, 2, 3].includes(apiVersion)) {
      return { kind: 'error', title: 'PowerView setup failed', error: 'PowerView API version must be 1, 2, or 3.' };
    }

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

    return { kind: 'done', title: 'PowerView 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 numberMetadata(metadataArg: Record<string, unknown>, keyArg: string): number | undefined {
    return this.numberValue(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) {
    return undefined;
  }
  try {
    const url = new URL(/^[a-z][a-z0-9+.-]*:\/\//i.test(valueArg) ? valueArg : `http://${valueArg}`);
    return { host: url.hostname, port: url.port ? Number(url.port) : undefined };
  } catch {
    return undefined;
  }
};
const snapshotValue = (valueArg: unknown): IHunterDouglasPowerViewSnapshot | undefined => valueArg && typeof valueArg === 'object' && 'hub' in valueArg && 'shades' in valueArg ? valueArg as IHunterDouglasPowerViewSnapshot : undefined;
const rawDataValue = (valueArg: unknown): Partial<IHunterDouglasPowerViewRawData> | undefined => valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg) ? valueArg as Partial<IHunterDouglasPowerViewRawData> : undefined;
