import type { IConfigFlow, IConfigFlowContext, IConfigFlowStep, IDiscoveryCandidate } from '../../core/types.js';
import type { IOpnsenseConfig, IOpnsenseSnapshot } from './opnsense.types.js';
import { opnsenseDefaultPort, opnsenseDefaultVerifySsl } from './opnsense.types.js';

export class OpnsenseConfigFlow implements IConfigFlow<IOpnsenseConfig> {
  public async start(candidateArg: IDiscoveryCandidate, contextArg: IConfigFlowContext): Promise<IConfigFlowStep<IOpnsenseConfig>> {
    void contextArg;
    return {
      kind: 'form',
      title: 'Connect OPNsense',
      description: 'Provide a local HTTPS OPNsense API endpoint. Snapshot/manual data is supported natively; live API success is only reported through an injected native client or command executor.',
      fields: [
        { name: 'url', label: candidateArg.host ? `URL or host (${candidateArg.host})` : 'URL or host', type: 'text', required: true },
        { name: 'port', label: `HTTPS port (${candidateArg.port || opnsenseDefaultPort})`, type: 'number' },
        { name: 'apiKey', label: 'API key', type: 'text' },
        { name: 'apiSecret', label: 'API secret', type: 'password' },
        { name: 'verifySsl', label: 'Verify TLS certificate', type: 'boolean' },
        { name: 'trackerInterfaces', label: 'Tracker interface descriptions, comma-separated', type: 'text' },
        { name: 'snapshotJson', label: 'Snapshot JSON', type: 'text' },
      ],
      submit: async (valuesArg) => this.submit(candidateArg, valuesArg),
    };
  }

  private async submit(candidateArg: IDiscoveryCandidate, valuesArg: Record<string, unknown>): Promise<IConfigFlowStep<IOpnsenseConfig>> {
    const snapshot = this.snapshotFromInput(valuesArg.snapshotJson || candidateArg.metadata?.snapshot);
    if (snapshot instanceof Error) {
      return { kind: 'error', title: 'Invalid OPNsense snapshot', error: snapshot.message };
    }

    const endpoint = parseHttpsEndpoint(stringValue(valuesArg.url) || candidateArg.metadata?.url as string | undefined || candidateArg.host || snapshot?.router.url || snapshot?.router.host || '');
    if (endpoint.error) {
      return { kind: 'error', title: 'Invalid OPNsense endpoint', error: endpoint.error };
    }
    if (!endpoint.host && !snapshot) {
      return { kind: 'error', title: 'OPNsense setup failed', error: 'OPNsense setup requires a local HTTPS host/URL or snapshot JSON.' };
    }

    const port = numberValue(valuesArg.port) || candidateArg.port || endpoint.port || snapshot?.router.port || (endpoint.host ? opnsenseDefaultPort : undefined);
    if (port !== undefined && (!Number.isInteger(port) || port < 1 || port > 65535)) {
      return { kind: 'error', title: 'Invalid OPNsense port', error: 'OPNsense port must be an integer between 1 and 65535.' };
    }

    const apiKey = stringValue(valuesArg.apiKey) || stringValue(candidateArg.metadata?.apiKey);
    const apiSecret = stringValue(valuesArg.apiSecret) || stringValue(candidateArg.metadata?.apiSecret);
    if (Boolean(apiKey) !== Boolean(apiSecret)) {
      return { kind: 'error', title: 'Incomplete OPNsense API credentials', error: 'OPNsense API key and API secret must be provided together.' };
    }

    const trackerInterfaces = listValue(valuesArg.trackerInterfaces) || listValue(candidateArg.metadata?.trackerInterfaces) || [];
    const verifySsl = booleanValue(valuesArg.verifySsl) ?? booleanValue(candidateArg.metadata?.verifySsl) ?? snapshot?.router.verifySsl ?? opnsenseDefaultVerifySsl;
    const url = endpoint.host ? endpointUrl(endpoint.host, port) : snapshot?.router.url;
    const config: IOpnsenseConfig = {
      url,
      host: endpoint.host || snapshot?.router.host,
      port,
      ssl: true,
      verifySsl,
      apiKey,
      apiSecret,
      trackerInterfaces,
      uniqueId: candidateArg.id || snapshot?.router.macAddress || snapshot?.router.id,
      name: candidateArg.name || snapshot?.router.name || endpoint.host || 'OPNsense',
      snapshot,
      metadata: {
        discoverySource: candidateArg.source,
        discoveryMetadata: candidateArg.metadata,
        liveHttpImplemented: false,
      },
    };

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

  private snapshotFromInput(valueArg: unknown): IOpnsenseSnapshot | undefined | Error {
    if (isOpnsenseSnapshot(valueArg)) {
      return valueArg;
    }
    const text = stringValue(valueArg);
    if (!text) {
      return undefined;
    }
    try {
      const parsed = JSON.parse(text) as IOpnsenseSnapshot;
      if (!isOpnsenseSnapshot(parsed)) {
        return new Error('Snapshot JSON must include router, interfaces, services, vpn, firewall, system, telemetry, sensors, and connected fields.');
      }
      return parsed;
    } catch (errorArg) {
      return errorArg instanceof Error ? errorArg : new Error(String(errorArg));
    }
  }
}

const parseHttpsEndpoint = (valueArg: string): { host?: string; port?: number; error?: string } => {
  const value = valueArg.trim();
  if (!value) {
    return {};
  }
  try {
    const parsed = new URL(value.includes('://') ? value : `https://${value}`);
    if (parsed.protocol !== 'https:') {
      return { error: 'OPNsense setup only supports local HTTPS candidates.' };
    }
    return {
      host: parsed.hostname,
      port: parsed.port ? Number(parsed.port) : undefined,
    };
  } catch {
    return { error: 'OPNsense endpoint must be a hostname, IP address, or HTTPS URL.' };
  }
};

const endpointUrl = (hostArg: string, portArg: number | undefined): string => {
  const port = portArg || opnsenseDefaultPort;
  return `https://${hostArg}${port === opnsenseDefaultPort ? '' : `:${port}`}`;
};

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

const numberValue = (valueArg: unknown): number | undefined => {
  if (typeof valueArg === 'number' && Number.isFinite(valueArg)) return Math.round(valueArg);
  if (typeof valueArg === 'string' && valueArg.trim() && Number.isFinite(Number(valueArg))) return Math.round(Number(valueArg));
  return undefined;
};

const booleanValue = (valueArg: unknown): boolean | undefined => {
  return typeof valueArg === 'boolean' ? valueArg : undefined;
};

const listValue = (valueArg: unknown): string[] | undefined => {
  if (Array.isArray(valueArg)) {
    const values = valueArg.filter((entryArg): entryArg is string => typeof entryArg === 'string' && entryArg.trim().length > 0).map((entryArg) => entryArg.trim());
    return values.length ? values : undefined;
  }
  const text = stringValue(valueArg);
  if (!text) {
    return undefined;
  }
  const values = text.split(',').map((entryArg) => entryArg.trim()).filter(Boolean);
  return values.length ? values : undefined;
};

const isOpnsenseSnapshot = (valueArg: unknown): valueArg is IOpnsenseSnapshot => {
  return Boolean(valueArg && typeof valueArg === 'object' && 'router' in valueArg && 'connected' in valueArg);
};
