import type { IConfigFlow, IConfigFlowContext, IConfigFlowStep, IDiscoveryCandidate } from '../../core/types.js';
import type { IGoodweConfig, IGoodweRawData, IGoodweSnapshot, TGoodweInverterFamily } from './goodwe.types.js';
import { goodweDefaultRetries, goodweDefaultTimeoutMs, goodweDefaultUdpPort, goodweDisplayName, goodweManufacturer } from './goodwe.types.js';

export class GoodweConfigFlow implements IConfigFlow<IGoodweConfig> {
  public async start(candidateArg: IDiscoveryCandidate, contextArg: IConfigFlowContext): Promise<IConfigFlowStep<IGoodweConfig>> {
    void contextArg;
    return {
      kind: 'form',
      title: 'Configure GoodWe inverter',
      description: 'Configure a local GoodWe inverter host, or use snapshot/raw data/injected client metadata. This TypeScript port does not implement the GoodWe UDP/TCP protocol, so host-only configs expose metadata and stay offline until a client or snapshot is supplied.',
      fields: [
        { name: 'host', label: 'Host', type: 'text' },
        { name: 'port', label: 'Port', type: 'number' },
        { name: 'modelFamily', label: 'Model family', type: 'select', options: [{ label: 'Auto/unknown', value: '' }, { label: 'ET/EH/BT/BH', value: 'ET' }, { label: 'ES/EM/BP', value: 'ES' }, { label: 'DT/MS/NS/XS', value: 'DT' }] },
        { name: 'name', label: 'Name', type: 'text' },
        { name: 'serialNumber', label: 'Serial number', type: 'text' },
      ],
      submit: async (valuesArg) => this.submit(candidateArg, valuesArg),
    };
  }

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

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

    const serialNumber = this.stringValue(valuesArg.serialNumber) || candidateArg.serialNumber || snapshot?.inverter.serialNumber || this.stringMetadata(metadata, 'serialNumber');
    const modelFamily = (this.stringValue(valuesArg.modelFamily) || snapshot?.inverter.modelFamily || this.stringMetadata(metadata, 'modelFamily')) as TGoodweInverterFamily | undefined;
    const config: IGoodweConfig = {
      host,
      port,
      timeoutMs: goodweDefaultTimeoutMs,
      retries: goodweDefaultRetries,
      name: this.stringValue(valuesArg.name) || candidateArg.name || snapshot?.inverter.name || this.stringMetadata(metadata, 'name') || goodweDisplayName,
      manufacturer: candidateArg.manufacturer || snapshot?.inverter.manufacturer || goodweManufacturer,
      model: candidateArg.model || snapshot?.inverter.model || this.stringMetadata(metadata, 'model'),
      modelFamily,
      uniqueId: candidateArg.id || snapshot?.inverter.id || serialNumber || (host ? `${host}:${port}` : undefined),
      serialNumber,
      firmware: snapshot?.inverter.firmware || this.stringMetadata(metadata, 'firmware'),
      armFirmware: snapshot?.inverter.armFirmware || this.stringMetadata(metadata, 'armFirmware'),
      ratedPower: snapshot?.inverter.ratedPower || this.numberMetadata(metadata, 'ratedPower'),
      snapshot,
      rawData,
      client: metadata.client as IGoodweConfig['client'],
      commandExecutor: metadata.commandExecutor as IGoodweConfig['commandExecutor'],
    };

    return { kind: 'done', title: 'GoodWe inverter 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 : `goodwe://${valueArg}`);
    return { host: url.hostname, port: url.port ? Number(url.port) : undefined };
  } catch {
    return undefined;
  }
};

const snapshotValue = (valueArg: unknown): IGoodweSnapshot | undefined => {
  return valueArg && typeof valueArg === 'object' && 'inverter' in valueArg && 'runtimeData' in valueArg ? valueArg as IGoodweSnapshot : undefined;
};

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