import type { IConfigFlow, IConfigFlowContext, IConfigFlowStep, IDiscoveryCandidate } from '../../core/types.js';
import type { IMotionEyeConfig, TMotionEyeProtocol } from './motioneye.types.js';
import { motionEyeDefaultPort, motionEyeDefaultTimeoutMs } from './motioneye.types.js';

export class MotionEyeConfigFlow implements IConfigFlow<IMotionEyeConfig> {
  public async start(candidateArg: IDiscoveryCandidate, contextArg: IConfigFlowContext): Promise<IConfigFlowStep<IMotionEyeConfig>> {
    void contextArg;
    return {
      kind: 'form',
      title: 'Connect motionEye',
      description: 'Configure the local motionEye HTTP endpoint. Use a base URL such as http://192.168.1.20:8765 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: 'adminUsername', label: 'Admin username', type: 'text' },
        { name: 'adminPassword', label: 'Admin password', type: 'password' },
        { name: 'surveillanceUsername', label: 'Surveillance username', type: 'text' },
        { name: 'surveillancePassword', label: 'Surveillance password', type: 'password' },
        { 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 || !endpoint.url) {
          return { kind: 'error', error: 'motionEye requires a base URL or host.' };
        }

        return {
          kind: 'done',
          title: 'motionEye configured',
          config: {
            protocol: endpoint.protocol,
            host: endpoint.host,
            port: endpoint.port,
            url: endpoint.url,
            adminUsername: this.stringValue(valuesArg.adminUsername) || this.stringMetadata(candidateArg, 'adminUsername'),
            adminPassword: this.stringValue(valuesArg.adminPassword) || this.stringMetadata(candidateArg, 'adminPassword'),
            surveillanceUsername: this.stringValue(valuesArg.surveillanceUsername) || this.stringMetadata(candidateArg, 'surveillanceUsername'),
            surveillancePassword: this.stringValue(valuesArg.surveillancePassword) || this.stringMetadata(candidateArg, 'surveillancePassword'),
            name: this.stringValue(valuesArg.name) || candidateArg.name || endpoint.host,
            uniqueId: candidateArg.id || endpoint.host,
            manufacturer: candidateArg.manufacturer || 'motionEye',
            model: candidateArg.model,
            timeoutMs: motionEyeDefaultTimeoutMs,
          },
        };
      },
    };
  }

  private endpoint(urlArg: string | undefined, hostArg: string | undefined, portArg: number | undefined, protocolArg: TMotionEyeProtocol | undefined): { protocol: TMotionEyeProtocol; 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 : motionEyeDefaultPort;
      const pathname = url.pathname && url.pathname !== '/' ? url.pathname.replace(/\/$/, '') : '';
      return { protocol, host: url.hostname, port, url: `${protocol}://${url.hostname}:${port}${pathname}` };
    }
    const protocol = protocolArg || 'http';
    const port = portArg || motionEyeDefaultPort;
    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): TMotionEyeProtocol | undefined {
    const protocol = candidateArg.metadata?.protocol;
    return protocol === 'http' || protocol === 'https' ? protocol : undefined;
  }
}

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