import { DiscoveryDescriptor } from '../../core/classes.discoverydescriptor.js';
import type { IDiscoveryCandidate, IDiscoveryMatch, IDiscoveryMatcher, IDiscoveryValidator } from '../../core/types.js';
import type { ICastManualEntry, ICastMdnsRecord } from './cast.types.js';

export class CastMdnsMatcher implements IDiscoveryMatcher<ICastMdnsRecord> {
  public id = 'cast-mdns-match';
  public source = 'mdns' as const;
  public description = 'Recognize Google Cast mDNS advertisements.';

  public async matches(recordArg: ICastMdnsRecord): Promise<IDiscoveryMatch> {
    const type = this.normalizeType(recordArg.type);
    const txtId = this.stripUuid(this.txt(recordArg, 'id'));
    const friendlyName = this.txt(recordArg, 'fn') || recordArg.name;
    const model = this.txt(recordArg, 'md');
    const matched = type === '_googlecast._tcp.local' || Boolean(txtId || model || friendlyName?.toLowerCase().includes('chromecast'));
    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'mDNS record is not a Google Cast advertisement.' };
    }
    return {
      matched: true,
      confidence: txtId ? 'certain' : 'high',
      reason: 'mDNS record matches Google Cast metadata.',
      normalizedDeviceId: txtId,
      candidate: {
        source: 'mdns',
        integrationDomain: 'cast',
        id: txtId,
        host: recordArg.host || recordArg.addresses?.[0],
        port: recordArg.port || 8009,
        name: friendlyName,
        manufacturer: 'Google',
        model,
        metadata: {
          mdnsName: recordArg.name,
          mdnsType: recordArg.type,
          txt: recordArg.txt,
        },
      },
    };
  }

  private txt(recordArg: ICastMdnsRecord, keyArg: string): string | undefined {
    return recordArg.txt?.[keyArg] || recordArg.txt?.[keyArg.toUpperCase()];
  }

  private normalizeType(valueArg?: string): string {
    return (valueArg || '').toLowerCase().replace(/\.$/, '');
  }

  private stripUuid(valueArg?: string): string | undefined {
    return valueArg?.replace(/^uuid:/i, '').replace(/-/g, '').toLowerCase();
  }
}

export class CastManualMatcher implements IDiscoveryMatcher<ICastManualEntry> {
  public id = 'cast-manual-match';
  public source = 'manual' as const;
  public description = 'Recognize manual Google Cast setup entries.';

  public async matches(inputArg: ICastManualEntry): Promise<IDiscoveryMatch> {
    const model = inputArg.model?.toLowerCase() || '';
    const manufacturer = inputArg.manufacturer?.toLowerCase() || '';
    const host = inputArg.host || inputArg.knownHosts?.[0];
    const matched = Boolean(host || inputArg.metadata?.cast || manufacturer.includes('google') || model.includes('chromecast') || model.includes('google home') || model.includes('nest'));
    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'Manual entry does not contain Google Cast setup hints.' };
    }
    return {
      matched: true,
      confidence: host ? 'high' : 'medium',
      reason: 'Manual entry can start Google Cast setup.',
      normalizedDeviceId: inputArg.id,
      candidate: {
        source: 'manual',
        integrationDomain: 'cast',
        id: inputArg.id,
        host,
        port: inputArg.port || 8009,
        name: inputArg.name,
        manufacturer: inputArg.manufacturer || 'Google',
        model: inputArg.model,
        metadata: {
          ...inputArg.metadata,
          knownHosts: inputArg.knownHosts,
        },
      },
    };
  }
}

export class CastCandidateValidator implements IDiscoveryValidator {
  public id = 'cast-candidate-validator';
  public description = 'Validate Google Cast candidate metadata.';

  public async validate(candidateArg: IDiscoveryCandidate): Promise<IDiscoveryMatch> {
    const manufacturer = candidateArg.manufacturer?.toLowerCase() || '';
    const model = candidateArg.model?.toLowerCase() || '';
    const matched = candidateArg.integrationDomain === 'cast' || manufacturer.includes('google') || model.includes('chromecast') || model.includes('google home') || model.includes('nest') || Boolean(candidateArg.metadata?.cast);
    return {
      matched,
      confidence: matched && candidateArg.host ? 'high' : matched ? 'medium' : 'low',
      reason: matched ? 'Candidate has Google Cast metadata.' : 'Candidate is not Google Cast.',
      candidate: matched ? candidateArg : undefined,
      normalizedDeviceId: candidateArg.id,
    };
  }
}

export const createCastDiscoveryDescriptor = (): DiscoveryDescriptor => {
  return new DiscoveryDescriptor({ integrationDomain: 'cast', displayName: 'Google Cast' })
    .addMatcher(new CastMdnsMatcher())
    .addMatcher(new CastManualMatcher())
    .addValidator(new CastCandidateValidator());
};
