import { DiscoveryDescriptor } from '../../core/classes.discoverydescriptor.js';
import type { IDiscoveryCandidate, IDiscoveryMatch, IDiscoveryMatcher, IDiscoveryValidator } from '../../core/types.js';
import type { IAdguardManualEntry, IAdguardSnapshot } from './adguard.types.js';
import { adguardDefaultPort } from './adguard.types.js';

export class AdguardManualMatcher implements IDiscoveryMatcher<IAdguardManualEntry> {
  public id = 'adguard-manual-match';
  public source = 'manual' as const;
  public description = 'Recognize manual AdGuard Home setup entries by host, domain, manufacturer, model, or metadata.';

  public async matches(inputArg: IAdguardManualEntry): Promise<IDiscoveryMatch> {
    const matched = isAdguardHint(inputArg) || Boolean(inputArg.host);
    if (!matched) {
      return {
        matched: false,
        confidence: 'low',
        reason: 'Manual entry does not contain AdGuard Home setup hints.',
      };
    }
    const id = inputArg.id || inputArg.host && `${inputArg.host}:${inputArg.port || adguardDefaultPort}`;
    return {
      matched: true,
      confidence: inputArg.host ? 'high' : 'medium',
      reason: 'Manual entry can start AdGuard Home setup.',
      normalizedDeviceId: id,
      candidate: {
        source: 'manual',
        integrationDomain: 'adguard',
        id,
        host: inputArg.host,
        port: inputArg.port || adguardDefaultPort,
        name: inputArg.name || 'AdGuard Home',
        manufacturer: 'AdGuard Team',
        model: inputArg.model || 'AdGuard Home',
        metadata: {
          ...inputArg.metadata,
          ssl: inputArg.ssl,
          verifySsl: inputArg.verifySsl,
          basePath: inputArg.basePath,
          snapshot: inputArg.snapshot,
        },
      },
    };
  }
}

export class AdguardCandidateValidator implements IDiscoveryValidator {
  public id = 'adguard-candidate-validator';
  public description = 'Validate AdGuard Home candidates before starting local HTTP setup.';

  public async validate(candidateArg: IDiscoveryCandidate): Promise<IDiscoveryMatch> {
    const matched = isAdguardHint(candidateArg) || candidateArg.integrationDomain === 'adguard';
    const snapshot = candidateArg.metadata?.snapshot;
    const hasSnapshot = isAdguardSnapshot(snapshot);
    const hasUsableAddress = Boolean(candidateArg.host && isValidPort(candidateArg.port || adguardDefaultPort));
    return {
      matched: matched && (hasUsableAddress || hasSnapshot),
      confidence: matched && candidateArg.id ? 'certain' : matched && hasUsableAddress ? 'high' : matched ? 'medium' : 'low',
      reason: matched
        ? hasUsableAddress || hasSnapshot ? 'Candidate has AdGuard Home metadata and a usable HTTP address or snapshot.' : 'Candidate has AdGuard Home metadata but no usable HTTP address.'
        : 'Candidate is not AdGuard Home.',
      candidate: matched && (hasUsableAddress || hasSnapshot) ? candidateArg : undefined,
      normalizedDeviceId: candidateArg.id || candidateArg.host && `${candidateArg.host}:${candidateArg.port || adguardDefaultPort}`,
    };
  }
}

export const createAdguardDiscoveryDescriptor = (): DiscoveryDescriptor => {
  return new DiscoveryDescriptor({
    integrationDomain: 'adguard',
    displayName: 'AdGuard Home',
  })
    .addMatcher(new AdguardManualMatcher())
    .addValidator(new AdguardCandidateValidator());
};

const isAdguardHint = (valueArg: { integrationDomain?: string; manufacturer?: string; model?: string; name?: string; metadata?: Record<string, unknown> }): boolean => {
  const manufacturer = valueArg.manufacturer?.toLowerCase() || '';
  const model = valueArg.model?.toLowerCase() || '';
  const name = valueArg.name?.toLowerCase() || '';
  return valueArg.integrationDomain === 'adguard'
    || manufacturer.includes('adguard')
    || model.includes('adguard')
    || name.includes('adguard')
    || Boolean(valueArg.metadata?.adguard);
};

const isValidPort = (valueArg: number): boolean => {
  return Number.isInteger(valueArg) && valueArg > 0 && valueArg <= 65535;
};

const isAdguardSnapshot = (valueArg: unknown): valueArg is IAdguardSnapshot => {
  return typeof valueArg === 'object' && valueArg !== null && 'status' in valueArg && 'online' in valueArg;
};
