import { DiscoveryDescriptor } from '../../core/classes.discoverydescriptor.js';
import type { IDiscoveryCandidate, IDiscoveryMatch, IDiscoveryMatcher, IDiscoveryValidator } from '../../core/types.js';
import type { IMadvrManualEntry, IMadvrMdnsRecord } from './madvr.types.js';
import { madvrDefaultPort } from './madvr.types.js';

export class MadvrManualMatcher implements IDiscoveryMatcher<IMadvrManualEntry> {
  public id = 'madvr-manual-match';
  public source = 'manual' as const;
  public description = 'Recognize manual madVR Envy setup entries.';

  public async matches(inputArg: IMadvrManualEntry): Promise<IDiscoveryMatch> {
    const matched = Boolean(inputArg.host || isMadvrHint(inputArg.manufacturer, inputArg.model, inputArg.name) || inputArg.metadata?.madvr);
    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'Manual entry does not contain madVR Envy setup hints.' };
    }
    const id = inputArg.id || inputArg.macAddress || inputArg.serialNumber || inputArg.host;
    return {
      matched: true,
      confidence: inputArg.host ? 'high' : 'medium',
      reason: 'Manual entry can start madVR Envy setup.',
      normalizedDeviceId: id,
      candidate: {
        source: 'manual',
        integrationDomain: 'madvr',
        id,
        host: inputArg.host,
        port: inputArg.port || madvrDefaultPort,
        name: inputArg.name || 'madVR Envy',
        manufacturer: inputArg.manufacturer || 'madVR',
        model: inputArg.model || 'Envy',
        macAddress: inputArg.macAddress,
        serialNumber: inputArg.serialNumber,
        metadata: inputArg.metadata,
      },
    };
  }
}

export class MadvrMdnsMatcher implements IDiscoveryMatcher<IMadvrMdnsRecord> {
  public id = 'madvr-mdns-match';
  public source = 'mdns' as const;
  public description = 'Recognize mDNS records that explicitly advertise madVR Envy metadata.';

  public async matches(recordArg: IMadvrMdnsRecord): Promise<IDiscoveryMatch> {
    const properties = { ...(recordArg.txt || {}), ...(recordArg.properties || {}) };
    const type = normalize(recordArg.type);
    const name = cleanName(valueForKey(properties, 'name') || recordArg.name);
    const manufacturer = valueForKey(properties, 'manufacturer') || valueForKey(properties, 'mf');
    const model = valueForKey(properties, 'model') || valueForKey(properties, 'modelName') || valueForKey(properties, 'md');
    const matched = type.includes('madvr') || isMadvrHint(manufacturer, model, name);
    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'mDNS record does not contain madVR Envy hints.' };
    }
    const macAddress = valueForKey(properties, 'mac') || valueForKey(properties, 'macAddress') || valueForKey(properties, 'mac_address');
    const id = valueForKey(properties, 'id') || macAddress || name;
    return {
      matched: true,
      confidence: id ? 'high' : 'medium',
      reason: 'mDNS record matches madVR Envy metadata.',
      normalizedDeviceId: id,
      candidate: {
        source: 'mdns',
        integrationDomain: 'madvr',
        id,
        host: recordArg.host,
        port: recordArg.port || madvrDefaultPort,
        name: name || 'madVR Envy',
        manufacturer: manufacturer || 'madVR',
        model: model || 'Envy',
        macAddress,
        metadata: { mdnsType: recordArg.type, mdnsName: recordArg.name, txt: properties },
      },
    };
  }
}

export class MadvrCandidateValidator implements IDiscoveryValidator {
  public id = 'madvr-candidate-validator';
  public description = 'Validate madVR Envy discovery candidates.';

  public async validate(candidateArg: IDiscoveryCandidate): Promise<IDiscoveryMatch> {
    const matched = candidateArg.integrationDomain === 'madvr'
      || Boolean(candidateArg.metadata?.madvr)
      || isMadvrHint(candidateArg.manufacturer, candidateArg.model, candidateArg.name);
    return {
      matched,
      confidence: matched && candidateArg.host ? 'high' : matched ? 'medium' : 'low',
      reason: matched ? 'Candidate has madVR Envy metadata.' : 'Candidate is not madVR Envy.',
      candidate: matched ? {
        ...candidateArg,
        integrationDomain: 'madvr',
        port: candidateArg.port || madvrDefaultPort,
      } : undefined,
      normalizedDeviceId: candidateArg.id || candidateArg.macAddress || candidateArg.serialNumber || candidateArg.host,
    };
  }
}

export const createMadvrDiscoveryDescriptor = (): DiscoveryDescriptor => {
  return new DiscoveryDescriptor({ integrationDomain: 'madvr', displayName: 'madVR Envy' })
    .addMatcher(new MadvrManualMatcher())
    .addMatcher(new MadvrMdnsMatcher())
    .addValidator(new MadvrCandidateValidator());
};

const isMadvrHint = (...valuesArg: Array<string | undefined>): boolean => {
  const value = valuesArg.filter(Boolean).join(' ').toLowerCase();
  return value.includes('madvr') || value.includes('mad vr') || value.includes('envy');
};

const normalize = (valueArg?: string): string => (valueArg || '').toLowerCase().replace(/\.$/, '');

const cleanName = (valueArg?: string): string | undefined => valueArg?.replace(/\._[^.]+\._tcp\.local\.?$/i, '').replace(/\.local\.?$/i, '').trim() || undefined;

const valueForKey = (recordArg: Record<string, string | undefined> | undefined, keyArg: string): string | undefined => {
  if (!recordArg) {
    return undefined;
  }
  const lowerKey = keyArg.toLowerCase();
  for (const [key, value] of Object.entries(recordArg)) {
    if (key.toLowerCase() === lowerKey && value) {
      return value;
    }
  }
  return undefined;
};
