import type { IConfigFlow, IConfigFlowContext, IConfigFlowStep, IDiscoveryCandidate } from '../../core/types.js';
import type { IHuaweiLteConfig, IHuaweiLteRawData, IHuaweiLteSnapshot } from './huawei_lte.types.js';
import { huaweiLteDefaultHttpPort, huaweiLteDefaultHttpsPort, huaweiLteDefaultTimeoutMs } from './huawei_lte.types.js';

export class HuaweiLteConfigFlow implements IConfigFlow<IHuaweiLteConfig> {
  public async start(candidateArg: IDiscoveryCandidate, contextArg: IConfigFlowContext): Promise<IConfigFlowStep<IHuaweiLteConfig>> {
    void contextArg;
    return {
      kind: 'form',
      title: 'Configure Huawei LTE',
      description: 'Configure a local Huawei HiLink router URL, or use snapshot/raw data from the discovery candidate. The runtime verifies live HTTP/API access on refresh.',
      fields: [
        { name: 'url', label: 'Router URL', type: 'text' },
        { name: 'verifySsl', label: 'Verify TLS certificate', type: 'boolean' },
        { name: 'username', label: 'Username', type: 'text' },
        { name: 'password', label: 'Password', type: 'password' },
        { name: 'unauthenticatedMode', label: 'Unauthenticated mode', type: 'boolean' },
        { name: 'trackWiredClients', label: 'Track wired clients', type: 'boolean' },
        { name: 'recipients', label: 'Default SMS recipients, comma separated', type: 'text' },
        { name: 'name', label: 'Name', type: 'text' },
      ],
      submit: async (valuesArg) => this.submit(candidateArg, valuesArg),
    };
  }

  private async submit(candidateArg: IDiscoveryCandidate, valuesArg: Record<string, unknown>): Promise<IConfigFlowStep<IHuaweiLteConfig>> {
    const metadata = candidateArg.metadata || {};
    const snapshot = snapshotValue(metadata.snapshot);
    const rawData = rawDataValue(metadata.rawData);
    const urlInput = this.stringValue(valuesArg.url) || this.stringMetadata(metadata, 'url');
    const parsedInput = parseEndpoint(urlInput);
    const host = parsedInput?.hostname || candidateArg.host || snapshot?.device.host;
    const ssl = parsedInput?.ssl ?? snapshot?.device.ssl ?? false;
    const port = parsedInput?.port || candidateArg.port || snapshot?.device.port || (ssl ? huaweiLteDefaultHttpsPort : huaweiLteDefaultHttpPort);
    const url = parsedInput ? normalizeEndpointUrl(urlInput) : host ? `${ssl ? 'https' : 'http'}://${hostForUrl(host)}${port !== (ssl ? huaweiLteDefaultHttpsPort : huaweiLteDefaultHttpPort) ? `:${port}` : ''}/` : undefined;
    const hasManualData = Boolean(snapshot || rawData || metadata.client);

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

    const config: IHuaweiLteConfig = {
      url,
      host,
      port,
      ssl,
      verifySsl: this.booleanValue(valuesArg.verifySsl) ?? this.booleanMetadata(metadata, 'verifySsl') ?? false,
      username: this.stringValue(valuesArg.username) || this.stringMetadata(metadata, 'username'),
      password: this.stringValue(valuesArg.password) || this.stringMetadata(metadata, 'password'),
      unauthenticatedMode: this.booleanValue(valuesArg.unauthenticatedMode) ?? this.booleanMetadata(metadata, 'unauthenticatedMode') ?? false,
      trackWiredClients: this.booleanValue(valuesArg.trackWiredClients) ?? this.booleanMetadata(metadata, 'trackWiredClients') ?? true,
      recipients: this.stringList(valuesArg.recipients) || this.stringList(metadata.recipients),
      timeoutMs: huaweiLteDefaultTimeoutMs,
      name: this.stringValue(valuesArg.name) || candidateArg.name || snapshot?.device.name || this.stringMetadata(metadata, 'name'),
      manufacturer: candidateArg.manufacturer || snapshot?.device.manufacturer || this.stringMetadata(metadata, 'manufacturer'),
      model: candidateArg.model || snapshot?.device.model || this.stringMetadata(metadata, 'model'),
      uniqueId: candidateArg.id || snapshot?.device.id || snapshot?.device.serialNumber || this.stringMetadata(metadata, 'upnpUdn') || (host ? `${host}:${port}` : undefined),
      serialNumber: candidateArg.serialNumber || snapshot?.device.serialNumber,
      macAddress: candidateArg.macAddress || snapshot?.device.macAddresses[0],
      macAddresses: snapshot?.device.macAddresses,
      upnpUdn: this.stringMetadata(metadata, 'upnpUdn'),
      snapshot,
      rawData,
      client: metadata.client as IHuaweiLteConfig['client'],
      commandExecutor: metadata.commandExecutor as IHuaweiLteConfig['commandExecutor'],
    };

    return { kind: 'done', title: 'Huawei LTE configured', config };
  }

  private stringValue(valueArg: unknown): string | undefined {
    return typeof valueArg === 'string' && valueArg.trim() ? valueArg.trim() : undefined;
  }

  private stringMetadata(metadataArg: Record<string, unknown>, keyArg: string): string | undefined {
    return this.stringValue(metadataArg[keyArg]);
  }

  private booleanValue(valueArg: unknown): boolean | undefined {
    if (typeof valueArg === 'boolean') {
      return valueArg;
    }
    if (typeof valueArg === 'string') {
      const normalized = valueArg.trim().toLowerCase();
      if (['true', 'yes', 'on', '1'].includes(normalized)) {
        return true;
      }
      if (['false', 'no', 'off', '0'].includes(normalized)) {
        return false;
      }
    }
    return undefined;
  }

  private booleanMetadata(metadataArg: Record<string, unknown>, keyArg: string): boolean | undefined {
    return typeof metadataArg[keyArg] === 'boolean' ? metadataArg[keyArg] as boolean : undefined;
  }

  private stringList(valueArg: unknown): string[] | undefined {
    if (Array.isArray(valueArg)) {
      return valueArg.map((itemArg) => this.stringValue(itemArg)).filter(Boolean) as string[];
    }
    const value = this.stringValue(valueArg);
    return value ? value.split(',').map((itemArg) => itemArg.trim()).filter(Boolean) : undefined;
  }
}

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

const normalizeEndpointUrl = (valueArg: string | undefined): string | undefined => {
  const parsed = valueArg ? parseEndpoint(valueArg) : undefined;
  if (!parsed) {
    return undefined;
  }
  const protocol = parsed.ssl ? 'https' : 'http';
  const defaultPort = parsed.ssl ? huaweiLteDefaultHttpsPort : huaweiLteDefaultHttpPort;
  return `${protocol}://${hostForUrl(parsed.hostname)}${parsed.port && parsed.port !== defaultPort ? `:${parsed.port}` : ''}/`;
};

const hostForUrl = (hostArg: string): string => hostArg.includes(':') && !hostArg.startsWith('[') ? `[${hostArg}]` : hostArg;
const validPort = (valueArg: number): boolean => Number.isInteger(valueArg) && valueArg > 0 && valueArg <= 65535;
const snapshotValue = (valueArg: unknown): IHuaweiLteSnapshot | undefined => valueArg && typeof valueArg === 'object' && 'device' in valueArg && 'connection' in valueArg ? valueArg as IHuaweiLteSnapshot : undefined;
const rawDataValue = (valueArg: unknown): Partial<IHuaweiLteRawData> | undefined => valueArg && typeof valueArg === 'object' && !Array.isArray(valueArg) ? valueArg as Partial<IHuaweiLteRawData> : undefined;
