import type { IConfigFlow, IConfigFlowContext, IConfigFlowStep, IDiscoveryCandidate } from '../../core/types.js';
import { HomeWizardClient } from './homewizard.classes.client.js';
import type { IHomeWizardConfig, IHomeWizardRawData, IHomeWizardSnapshot, THomeWizardApiVersion, THomeWizardProtocol } from './homewizard.types.js';
import { homeWizardDefaultHttpPort, homeWizardDefaultHttpsPort } from './homewizard.types.js';

export class HomeWizardConfigFlow implements IConfigFlow<IHomeWizardConfig> {
  public async start(candidateArg: IDiscoveryCandidate, contextArg: IConfigFlowContext): Promise<IConfigFlowStep<IHomeWizardConfig>> {
    void contextArg;
    return {
      kind: 'form',
      title: 'Configure HomeWizard',
      description: 'Configure a local HomeWizard Energy endpoint. API v1 uses HTTP after enabling Local API in the app; API v2 uses a bearer token obtained by pressing the device button.',
      fields: [
        { name: 'host', label: 'Host', type: 'text', required: false },
        { name: 'port', label: 'Port', type: 'number', required: false },
        { name: 'apiVersion', label: 'API version', type: 'select', required: false, options: [{ label: 'Auto', value: 'auto' }, { label: 'v1 HTTP', value: 'v1' }, { label: 'v2 HTTPS', value: 'v2' }] },
        { name: 'token', label: 'API v2 token', type: 'password', required: false },
        { name: 'tokenName', label: 'Token name', type: 'text', required: false },
        { name: 'name', label: 'Name', type: 'text', required: false },
      ],
      submit: async (valuesArg) => this.submit(candidateArg, valuesArg),
    };
  }

  private async submit(candidateArg: IDiscoveryCandidate, valuesArg: Record<string, unknown>): Promise<IConfigFlowStep<IHomeWizardConfig>> {
    const metadata = candidateArg.metadata || {};
    const snapshot = snapshotValue(metadata.snapshot);
    const rawData = rawDataValue(metadata.rawData);
    const parsed = parseEndpoint(this.stringValue(valuesArg.host) || candidateArg.host);
    let token = this.stringValue(valuesArg.token) || this.stringValue(metadata.token) || this.stringValue(metadata.authorizationToken);
    const apiVersion = this.inferApiVersion(valuesArg.apiVersion || metadata.apiVersion || snapshot?.apiVersion, token, metadata.protocol, candidateArg.port || parsed?.port);
    const protocol = this.protocol(metadata.protocol, apiVersion);
    const host = parsed?.host || this.stringValue(valuesArg.host) || candidateArg.host || snapshot?.host;
    const port = this.numberValue(valuesArg.port) || parsed?.port || candidateArg.port || snapshot?.port || (apiVersion === 'v2' ? homeWizardDefaultHttpsPort : homeWizardDefaultHttpPort);
    const hasManualData = Boolean(snapshot || rawData || metadata.client);

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

    const config: IHomeWizardConfig = {
      host,
      port,
      protocol,
      apiVersion,
      token,
      timeoutMs: 5000,
      name: this.stringValue(valuesArg.name) || candidateArg.name || snapshot?.device.productName || this.stringValue(metadata.productName),
      productName: this.stringValue(metadata.productName) || snapshot?.device.productName,
      productType: this.stringValue(metadata.productType) || candidateArg.model || snapshot?.device.productType,
      serial: this.stringValue(metadata.serial) || candidateArg.serialNumber || snapshot?.device.serial,
      uniqueId: candidateArg.id || snapshot?.device.id || (host ? `${host}:${port}` : undefined),
      identifier: this.stringValue(metadata.identifier) || snapshot?.device.verificationId,
      verifyTls: metadata.verifyTls === true,
      snapshot,
      rawData,
      client: metadata.client as IHomeWizardConfig['client'],
      commandExecutor: metadata.commandExecutor as IHomeWizardConfig['commandExecutor'],
    };

    if (host && apiVersion === 'v2' && !token && !hasManualData) {
      token = await HomeWizardClient.requestToken(host, {
        port,
        protocol,
        name: this.stringValue(valuesArg.tokenName) || 'smarthome-exchange',
        timeoutMs: 2000,
        verifyTls: config.verifyTls,
      }).catch(() => undefined);
      if (!token) {
        return {
          kind: 'wait',
          title: 'Authorize HomeWizard',
          description: 'Press the HomeWizard device button, then submit again to request an API v2 token. No configuration is stored until a token or another usable local data source is available.',
        };
      }
      config.token = token;
    }

    if (host && !hasManualData) {
      const snapshotResult = await new HomeWizardClient(config).getSnapshot(true);
      if (!snapshotResult.online || snapshotResult.error) {
        return { kind: 'error', title: 'HomeWizard setup failed', error: snapshotResult.error || 'HomeWizard local API did not return a usable snapshot.' };
      }
      config.productName = config.productName || snapshotResult.device.productName;
      config.productType = config.productType || snapshotResult.device.productType;
      config.serial = config.serial || snapshotResult.device.serial;
      config.uniqueId = `${snapshotResult.device.productType}_${snapshotResult.device.serial}`;
      config.identifier = config.identifier || snapshotResult.device.verificationId;
    }

    return { kind: 'done', title: 'HomeWizard 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 apiVersion(valueArg: unknown): THomeWizardApiVersion {
    return valueArg === 'v1' || valueArg === 'v2' ? valueArg : 'auto';
  }

  private inferApiVersion(valueArg: unknown, tokenArg: string | undefined, protocolArg: unknown, portArg: number | undefined): THomeWizardApiVersion {
    const explicit = this.apiVersion(valueArg);
    if (explicit !== 'auto') {
      return explicit;
    }
    if (tokenArg || protocolArg === 'https' || portArg === homeWizardDefaultHttpsPort) {
      return 'v2';
    }
    return 'auto';
  }

  private protocol(valueArg: unknown, apiVersionArg: THomeWizardApiVersion): THomeWizardProtocol | undefined {
    if (valueArg === 'http' || valueArg === 'https') {
      return valueArg;
    }
    return apiVersionArg === 'v2' ? 'https' : undefined;
  }

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

const snapshotValue = (valueArg: unknown): IHomeWizardSnapshot | undefined => valueArg && typeof valueArg === 'object' && 'device' in valueArg && 'measurement' in valueArg ? valueArg as IHomeWizardSnapshot : undefined;

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

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