import { DiscoveryDescriptor } from '../../core/classes.discoverydescriptor.js';
import type { IDiscoveryCandidate, IDiscoveryContext, IDiscoveryMatch, IDiscoveryMatcher, IDiscoveryValidator } from '../../core/types.js';
import type { IAndroidIpWebcamManualEntry, TAndroidIpWebcamProtocol } from './android_ip_webcam.types.js';
import { androidIpWebcamDefaultPort } from './android_ip_webcam.types.js';

export class AndroidIpWebcamManualMatcher implements IDiscoveryMatcher<IAndroidIpWebcamManualEntry> {
  public id = 'android-ip-webcam-manual-match';
  public source = 'manual' as const;
  public description = 'Recognize manual Android IP Webcam host or base URL entries.';

  public async matches(inputArg: IAndroidIpWebcamManualEntry, contextArg: IDiscoveryContext): Promise<IDiscoveryMatch> {
    void contextArg;
    const endpoint = endpointFromInput(inputArg);
    const hasHint = Boolean(endpoint.host || inputArg.metadata?.androidIpWebcam || inputArg.metadata?.android_ip_webcam);
    if (!hasHint) {
      return { matched: false, confidence: 'low', reason: 'Manual Android IP Webcam entry requires host, url, or android_ip_webcam metadata.' };
    }

    const normalizedDeviceId = inputArg.id || endpoint.host || endpoint.url;
    return {
      matched: true,
      confidence: endpoint.host ? 'high' : 'medium',
      reason: endpoint.url ? 'Manual entry contains an Android IP Webcam base URL.' : 'Manual entry contains an Android IP Webcam host.',
      normalizedDeviceId,
      candidate: {
        source: 'manual',
        integrationDomain: 'android_ip_webcam',
        id: normalizedDeviceId,
        host: endpoint.host,
        port: endpoint.port,
        name: inputArg.name || endpoint.host,
        manufacturer: inputArg.manufacturer || 'Android IP Webcam',
        model: inputArg.model,
        metadata: {
          ...inputArg.metadata,
          protocol: endpoint.protocol,
          url: endpoint.url,
          username: inputArg.username,
          password: inputArg.password,
          discoveryProtocol: 'manual',
        },
      },
      metadata: {
        protocol: endpoint.protocol,
        url: endpoint.url,
      },
    };
  }
}

export class AndroidIpWebcamCandidateValidator implements IDiscoveryValidator {
  public id = 'android-ip-webcam-candidate-validator';
  public description = 'Validate that a discovery candidate has a usable Android IP Webcam host or URL.';

  public async validate(candidateArg: IDiscoveryCandidate, contextArg: IDiscoveryContext): Promise<IDiscoveryMatch> {
    void contextArg;
    if (candidateArg.integrationDomain && candidateArg.integrationDomain !== 'android_ip_webcam') {
      return { matched: false, confidence: 'low', reason: `Candidate belongs to ${candidateArg.integrationDomain}, not Android IP Webcam.` };
    }
    const endpoint = endpointFromCandidate(candidateArg);
    if (!endpoint.host) {
      return { matched: false, confidence: 'low', reason: 'Android IP Webcam candidates require a host or URL.' };
    }
    if (!Number.isInteger(endpoint.port) || endpoint.port < 1 || endpoint.port > 65535) {
      return { matched: false, confidence: 'low', reason: 'Android IP Webcam candidate has an invalid port.' };
    }
    return {
      matched: true,
      confidence: candidateArg.source === 'manual' ? 'high' : 'medium',
      reason: 'Candidate has enough Android IP Webcam metadata to start configuration.',
      normalizedDeviceId: candidateArg.id || endpoint.host,
      candidate: {
        ...candidateArg,
        integrationDomain: 'android_ip_webcam',
        host: endpoint.host,
        port: endpoint.port,
        manufacturer: candidateArg.manufacturer || 'Android IP Webcam',
        metadata: {
          ...candidateArg.metadata,
          protocol: endpoint.protocol,
          url: endpoint.url,
        },
      },
      metadata: {
        manualSupported: candidateArg.source === 'manual',
        protocol: endpoint.protocol,
        url: endpoint.url,
      },
    };
  }
}

export const createAndroidIpWebcamDiscoveryDescriptor = (): DiscoveryDescriptor => {
  return new DiscoveryDescriptor({ integrationDomain: 'android_ip_webcam', displayName: 'Android IP Webcam' })
    .addMatcher(new AndroidIpWebcamManualMatcher())
    .addValidator(new AndroidIpWebcamCandidateValidator());
};

const endpointFromInput = (inputArg: IAndroidIpWebcamManualEntry): { protocol: TAndroidIpWebcamProtocol; host?: string; port: number; url?: string } => {
  const url = safeUrl(inputArg.url || inputArg.host);
  if (url) {
    return {
      protocol: url.protocol === 'https:' ? 'https' : 'http',
      host: url.hostname,
      port: url.port ? Number(url.port) : url.protocol === 'https:' ? 443 : androidIpWebcamDefaultPort,
      url: url.toString().replace(/\/$/, ''),
    };
  }
  return {
    protocol: inputArg.protocol || 'http',
    host: inputArg.host,
    port: inputArg.port || androidIpWebcamDefaultPort,
    url: inputArg.host ? `${inputArg.protocol || 'http'}://${inputArg.host}:${inputArg.port || androidIpWebcamDefaultPort}` : undefined,
  };
};

const endpointFromCandidate = (candidateArg: IDiscoveryCandidate): { protocol: TAndroidIpWebcamProtocol; host?: string; port: number; url?: string } => {
  const metadataUrl = typeof candidateArg.metadata?.url === 'string' ? candidateArg.metadata.url : undefined;
  const metadataProtocol = candidateArg.metadata?.protocol === 'https' ? 'https' : 'http';
  const url = safeUrl(metadataUrl || candidateArg.host);
  if (url) {
    return {
      protocol: url.protocol === 'https:' ? 'https' : 'http',
      host: url.hostname,
      port: url.port ? Number(url.port) : url.protocol === 'https:' ? 443 : androidIpWebcamDefaultPort,
      url: url.toString().replace(/\/$/, ''),
    };
  }
  return {
    protocol: metadataProtocol,
    host: candidateArg.host,
    port: candidateArg.port || androidIpWebcamDefaultPort,
    url: candidateArg.host ? `${metadataProtocol}://${candidateArg.host}:${candidateArg.port || androidIpWebcamDefaultPort}` : metadataUrl,
  };
};

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