import type { IConfigFlow, IConfigFlowContext, IConfigFlowStep, IDiscoveryCandidate } from '../../core/types.js';
import { DelugeClient } from './deluge.classes.client.js';
import type { IDelugeConfig, IDelugeRawData, IDelugeSnapshot, TDelugeProtocol } from './deluge.types.js';
import { delugeDefaultRpcPort, delugeDefaultTimeoutMs, delugeDefaultWebPort } from './deluge.types.js';

export class DelugeConfigFlow implements IConfigFlow<IDelugeConfig> {
  public async start(candidateArg: IDiscoveryCandidate, contextArg: IConfigFlowContext): Promise<IConfigFlowStep<IDelugeConfig>> {
    void contextArg;
    return {
      kind: 'form',
      title: 'Connect Deluge',
      description: 'Configure a local Deluge Web JSON-RPC endpoint. Use a base URL such as http://192.168.1.20:8112 or host plus web port.',
      fields: [
        { name: 'url', label: 'Deluge Web URL', type: 'text' },
        { name: 'host', label: 'Host', type: 'text' },
        { name: 'webPort', label: 'Web port', type: 'number' },
        { name: 'password', label: 'Deluge Web password', type: 'password' },
        { name: 'daemonHost', label: 'Daemon host', type: 'text' },
        { name: 'daemonPort', label: 'Daemon RPC port', type: 'number' },
        { name: 'username', label: 'Daemon username', type: 'text' },
        { name: 'daemonPassword', label: 'Daemon password', type: 'password' },
        { name: 'name', label: 'Name', type: 'text' },
      ],
      submit: async (valuesArg) => this.submit(candidateArg, valuesArg),
    };
  }

  private async submit(candidateArg: IDiscoveryCandidate, valuesArg: Record<string, unknown>): Promise<IConfigFlowStep<IDelugeConfig>> {
    const snapshot = snapshotMetadata(candidateArg);
    if (snapshot) {
      return { kind: 'done', title: 'Deluge configured', config: { snapshot, name: snapshot.device.name, uniqueId: snapshot.device.id } };
    }
    const rawData = rawDataMetadata(candidateArg);
    const injectedClient = candidateArg.metadata?.client;
    if (injectedClient && typeof injectedClient === 'object') {
      return { kind: 'done', title: 'Deluge configured', config: { client: injectedClient as IDelugeConfig['client'], name: stringValue(valuesArg.name) || candidateArg.name || 'Deluge', uniqueId: candidateArg.id } };
    }
    if (rawData) {
      return { kind: 'done', title: 'Deluge configured', config: { rawData, name: stringValue(valuesArg.name) || candidateArg.name || 'Deluge', uniqueId: candidateArg.id } };
    }

    const resolvedEndpoint = resolveEndpoint(stringValue(valuesArg.url) || stringMetadata(candidateArg, 'url'), stringValue(valuesArg.host) || candidateArg.host, numberValue(valuesArg.webPort) || candidateArg.port || numberMetadata(candidateArg, 'webPort') || numberMetadata(candidateArg, 'web_port'), protocolMetadata(candidateArg));
    if (!resolvedEndpoint.host) {
      return { kind: 'error', error: 'Deluge setup requires a Deluge Web URL, host, snapshot, raw data, or injected client.' };
    }
    const password = stringValue(valuesArg.password) || stringMetadata(candidateArg, 'password') || stringMetadata(candidateArg, 'webPassword');
    if (!password) {
      return { kind: 'error', error: 'Deluge Web JSON-RPC setup requires the Deluge Web password.' };
    }
    const config: IDelugeConfig = {
      protocol: resolvedEndpoint.protocol,
      host: resolvedEndpoint.host,
      webPort: resolvedEndpoint.webPort,
      url: resolvedEndpoint.url,
      password,
      daemonHost: stringValue(valuesArg.daemonHost) || stringMetadata(candidateArg, 'daemonHost') || resolvedEndpoint.host,
      daemonPort: numberValue(valuesArg.daemonPort) || numberMetadata(candidateArg, 'daemonPort') || numberMetadata(candidateArg, 'rpcPort') || delugeDefaultRpcPort,
      username: stringValue(valuesArg.username) || stringMetadata(candidateArg, 'username'),
      daemonPassword: stringValue(valuesArg.daemonPassword) || stringMetadata(candidateArg, 'daemonPassword') || stringMetadata(candidateArg, 'rpcPassword'),
      name: stringValue(valuesArg.name) || candidateArg.name || resolvedEndpoint.host,
      uniqueId: candidateArg.id || resolvedEndpoint.host,
      manufacturer: candidateArg.manufacturer || 'Deluge',
      model: candidateArg.model,
      timeoutMs: delugeDefaultTimeoutMs,
    };
    try {
      const liveSnapshot = await new DelugeClient(config).validateConnection();
      return { kind: 'done', title: 'Deluge configured', config: { ...config, uniqueId: liveSnapshot.device.id, name: config.name || liveSnapshot.device.name } };
    } catch (errorArg) {
      return { kind: 'error', error: errorArg instanceof Error ? errorArg.message : String(errorArg) };
    }
  }
}

const resolveEndpoint = (urlArg: string | undefined, hostArg: string | undefined, portArg: number | undefined, protocolArg: TDelugeProtocol | undefined): { protocol: TDelugeProtocol; host?: string; webPort: number; url?: string } => {
  const url = safeUrl(urlArg || hostArg);
  if (url) {
    const protocol = url.protocol === 'https:' ? 'https' : 'http';
    const webPort = url.port ? Number(url.port) : protocol === 'https' ? 443 : delugeDefaultWebPort;
    return { protocol, host: url.hostname, webPort, url: url.toString().replace(/\/$/, '') };
  }
  const protocol = protocolArg || 'http';
  const webPort = portArg || delugeDefaultWebPort;
  return { protocol, host: hostArg, webPort, url: hostArg ? `${protocol}://${hostArg}:${webPort}` : undefined };
};

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 valueArg;
  }
  if (typeof valueArg === 'string' && valueArg.trim()) {
    const parsed = Number(valueArg);
    return Number.isFinite(parsed) ? parsed : undefined;
  }
  return undefined;
};

const stringMetadata = (candidateArg: IDiscoveryCandidate, keyArg: string): string | undefined => {
  const value = candidateArg.metadata?.[keyArg];
  return stringValue(value);
};

const numberMetadata = (candidateArg: IDiscoveryCandidate, keyArg: string): number | undefined => {
  return numberValue(candidateArg.metadata?.[keyArg]);
};

const protocolMetadata = (candidateArg: IDiscoveryCandidate): TDelugeProtocol | undefined => {
  const protocol = candidateArg.metadata?.protocol;
  return protocol === 'http' || protocol === 'https' ? protocol : undefined;
};

const snapshotMetadata = (candidateArg: IDiscoveryCandidate): IDelugeSnapshot | undefined => {
  const snapshot = candidateArg.metadata?.snapshot;
  return snapshot && typeof snapshot === 'object' && 'device' in snapshot && 'torrents' in snapshot ? snapshot as IDelugeSnapshot : undefined;
};

const rawDataMetadata = (candidateArg: IDiscoveryCandidate): Partial<IDelugeRawData> | undefined => {
  const rawData = candidateArg.metadata?.rawData;
  return rawData && typeof rawData === 'object' ? rawData as Partial<IDelugeRawData> : undefined;
};

const safeUrl = (valueArg: string | undefined): URL | undefined => {
  if (!valueArg) {
    return undefined;
  }
  try {
    return new URL(valueArg);
  } catch {
    return undefined;
  }
};
