import type { IConfigFlow, IConfigFlowContext, IConfigFlowStep, IDiscoveryCandidate } from '../../core/types.js';
import type { IAmcrestConfig, TAmcrestAuthScheme, TAmcrestProtocol, TAmcrestResolution, TAmcrestStreamSource } from './amcrest.types.js';
import { amcrestDefaultPort, amcrestDefaultTimeoutMs } from './amcrest.types.js';

export class AmcrestConfigFlow implements IConfigFlow<IAmcrestConfig> {
  public async start(candidateArg: IDiscoveryCandidate, contextArg: IConfigFlowContext): Promise<IConfigFlowStep<IAmcrestConfig>> {
    void contextArg;
    return {
      kind: 'form',
      title: 'Connect Amcrest camera',
      description: 'Configure the local Amcrest HTTP CGI endpoint. Use a base URL or host plus port.',
      fields: [
        { name: 'url', label: 'Base URL', type: 'text' },
        { name: 'host', label: 'Host', type: 'text' },
        { name: 'port', label: 'Port', type: 'number' },
        { name: 'username', label: 'Username', type: 'text', required: true },
        { name: 'password', label: 'Password', type: 'password', required: true },
        { name: 'authScheme', label: 'Authentication', type: 'select', options: [{ label: 'Auto', value: 'auto' }, { label: 'Basic', value: 'basic' }, { label: 'Digest', value: 'digest' }] },
        { name: 'streamSource', label: 'Stream source', type: 'select', options: [{ label: 'Snapshot', value: 'snapshot' }, { label: 'MJPEG', value: 'mjpeg' }, { label: 'RTSP', value: 'rtsp' }] },
        { name: 'resolution', label: 'Resolution', type: 'select', options: [{ label: 'High', value: 'high' }, { label: 'Low', value: 'low' }] },
        { name: 'channel', label: 'Channel', type: 'number' },
        { name: 'name', label: 'Name', type: 'text' },
      ],
      submit: async (valuesArg) => {
        const urlValue = this.stringValue(valuesArg.url) || this.stringMetadata(candidateArg, 'url');
        const endpoint = this.endpoint(urlValue, this.stringValue(valuesArg.host) || candidateArg.host, this.numberValue(valuesArg.port) || candidateArg.port, this.protocolMetadata(candidateArg));
        if (!endpoint.host) {
          return { kind: 'error', error: 'Amcrest requires a base URL or host.' };
        }
        const username = this.stringValue(valuesArg.username) || this.stringMetadata(candidateArg, 'username');
        const password = this.stringValue(valuesArg.password) || this.stringMetadata(candidateArg, 'password');
        if (!username || password === undefined) {
          return { kind: 'error', error: 'Amcrest requires username and password.' };
        }
        const channel = this.numberValue(valuesArg.channel);
        return {
          kind: 'done',
          title: 'Amcrest camera configured',
          config: {
            protocol: endpoint.protocol,
            host: endpoint.host,
            port: endpoint.port,
            url: endpoint.url,
            username,
            password,
            authScheme: this.authSchemeValue(valuesArg.authScheme) || 'auto',
            streamSource: this.streamSourceValue(valuesArg.streamSource) || 'snapshot',
            resolution: this.resolutionValue(valuesArg.resolution) || 'high',
            channel: channel === undefined ? 0 : Math.max(0, Math.floor(channel)),
            name: this.stringValue(valuesArg.name) || candidateArg.name || endpoint.host,
            uniqueId: candidateArg.id || candidateArg.macAddress || candidateArg.serialNumber || endpoint.host,
            manufacturer: candidateArg.manufacturer || 'Amcrest',
            model: candidateArg.model,
            timeoutMs: amcrestDefaultTimeoutMs,
            controlLight: true,
          },
        };
      },
    };
  }

  private endpoint(urlArg: string | undefined, hostArg: string | undefined, portArg: number | undefined, protocolArg: TAmcrestProtocol | undefined): { protocol: TAmcrestProtocol; host?: string; port: number; url?: string } {
    const url = safeUrl(urlArg || hostArg);
    if (url) {
      const protocol = url.protocol === 'https:' ? 'https' : 'http';
      const port = url.port ? Number(url.port) : protocol === 'https' ? 443 : amcrestDefaultPort;
      return { protocol, host: url.hostname, port, url: `${protocol}://${url.hostname}:${port}` };
    }
    const protocol = protocolArg || 'http';
    const port = portArg || amcrestDefaultPort;
    return { protocol, host: hostArg, port, url: hostArg ? `${protocol}://${hostArg}:${port}` : undefined };
  }

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

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

  private stringMetadata(candidateArg: IDiscoveryCandidate, keyArg: string): string | undefined {
    const value = candidateArg.metadata?.[keyArg];
    return typeof value === 'string' && value.trim() ? value.trim() : undefined;
  }

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

  private authSchemeValue(valueArg: unknown): TAmcrestAuthScheme | undefined {
    return valueArg === 'auto' || valueArg === 'basic' || valueArg === 'digest' ? valueArg : undefined;
  }

  private streamSourceValue(valueArg: unknown): TAmcrestStreamSource | undefined {
    return valueArg === 'snapshot' || valueArg === 'mjpeg' || valueArg === 'rtsp' ? valueArg : undefined;
  }

  private resolutionValue(valueArg: unknown): TAmcrestResolution | undefined {
    return valueArg === 'high' || valueArg === 'low' ? valueArg : undefined;
  }
}

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