import { DiscoveryDescriptor } from '../../core/classes.discoverydescriptor.js';
import type { IDiscoveryCandidate, IDiscoveryMatch, IDiscoveryMatcher, IDiscoveryValidator } from '../../core/types.js';
import type { IDenonManualEntry, IDenonSnapshot } from './denon.types.js';
import { denonDefaultName, denonDefaultPort, denonDomain } from './denon.types.js';

export class DenonManualMatcher implements IDiscoveryMatcher<IDenonManualEntry> {
  public id = 'denon-manual-match';
  public source = 'manual' as const;
  public description = 'Recognize manual Denon Network Receiver setup entries.';

  public async matches(inputArg: IDenonManualEntry): Promise<IDiscoveryMatch> {
    const metadata = inputArg.metadata || {};
    const snapshot = inputArg.snapshot || (metadata.snapshot as IDenonSnapshot | undefined);
    const matched = Boolean(
      inputArg.host
      || snapshot
      || inputArg.commandExecutor
      || inputArg.client
      || metadata.denon
      || inputArg.manufacturer?.toLowerCase().includes('denon')
      || inputArg.model?.toLowerCase().includes('denon')
    );

    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'Manual entry does not contain Denon Network Receiver setup hints.' };
    }

    const id = inputArg.id || snapshot?.receiverInfo.serialNumber || inputArg.serialNumber || inputArg.host;
    return {
      matched: true,
      confidence: inputArg.host || snapshot ? 'high' : 'medium',
      reason: 'Manual entry can start Denon Network Receiver setup.',
      normalizedDeviceId: id,
      candidate: {
        source: 'manual',
        integrationDomain: denonDomain,
        id,
        host: inputArg.host || snapshot?.receiverInfo.host,
        port: inputArg.port || snapshot?.receiverInfo.port || denonDefaultPort,
        name: snapshot?.receiverInfo.name || inputArg.name || denonDefaultName,
        manufacturer: inputArg.manufacturer || snapshot?.receiverInfo.manufacturer || 'Denon',
        model: inputArg.model || snapshot?.receiverInfo.modelName,
        serialNumber: inputArg.serialNumber || snapshot?.receiverInfo.serialNumber,
        metadata: {
          ...metadata,
          snapshot,
          sourceMap: inputArg.sourceMap || metadata.sourceMap,
          client: inputArg.client || metadata.client,
          commandExecutor: inputArg.commandExecutor || metadata.commandExecutor,
        },
      },
    };
  }
}

export class DenonCandidateValidator implements IDiscoveryValidator {
  public id = 'denon-candidate-validator';
  public description = 'Validate Denon Network Receiver candidate metadata.';

  public async validate(candidateArg: IDiscoveryCandidate): Promise<IDiscoveryMatch> {
    const metadata = candidateArg.metadata || {};
    const snapshot = metadata.snapshot as IDenonSnapshot | undefined;
    const matched = candidateArg.integrationDomain === denonDomain
      || Boolean(candidateArg.host)
      || Boolean(snapshot)
      || Boolean(metadata.commandExecutor)
      || Boolean(metadata.client)
      || candidateArg.manufacturer?.toLowerCase().includes('denon') === true
      || candidateArg.model?.toLowerCase().includes('denon') === true;

    return {
      matched,
      confidence: matched && (candidateArg.host || snapshot) ? 'high' : matched ? 'medium' : 'low',
      reason: matched ? 'Candidate has usable Denon Network Receiver setup data.' : 'Candidate is not a Denon Network Receiver.',
      candidate: matched ? candidateArg : undefined,
      normalizedDeviceId: candidateArg.id || candidateArg.serialNumber || snapshot?.receiverInfo.serialNumber || candidateArg.host,
    };
  }
}

export const createDenonDiscoveryDescriptor = (): DiscoveryDescriptor => {
  return new DiscoveryDescriptor({ integrationDomain: denonDomain, displayName: 'Denon Network Receivers' })
    .addMatcher(new DenonManualMatcher())
    .addValidator(new DenonCandidateValidator());
};
