import { DiscoveryDescriptor } from '../../core/classes.discoverydescriptor.js';
import type { IDiscoveryCandidate, IDiscoveryMatch, IDiscoveryMatcher, IDiscoveryValidator } from '../../core/types.js';
import type { IMatterManualEntry, IMatterMdnsRecord } from './matter.types.js';

const defaultMatterServerUrl = 'ws://localhost:5580/ws';

export class MatterMdnsMatcher implements IDiscoveryMatcher<IMatterMdnsRecord> {
  public id = 'matter-mdns-match';
  public source = 'mdns' as const;
  public description = 'Recognize Matter zeroconf advertisements.';

  public async matches(recordArg: IMatterMdnsRecord): Promise<IDiscoveryMatch> {
    const type = this.normalizeType(recordArg.type);
    const matched = type === '_matter._tcp.local' || type === '_matterc._udp.local';
    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'mDNS record is not a Matter advertisement.' };
    }
    const instanceName = this.txt(recordArg, 'dn') || this.txt(recordArg, 'D') || recordArg.name;
    return {
      matched: true,
      confidence: 'high',
      reason: 'mDNS record matches Matter zeroconf metadata.',
      normalizedDeviceId: recordArg.name || recordArg.host,
      candidate: {
        source: 'mdns',
        integrationDomain: 'matter',
        id: recordArg.name || recordArg.host,
        host: recordArg.host || recordArg.addresses?.[0],
        port: recordArg.port,
        name: instanceName || 'Matter device',
        manufacturer: 'Matter',
        model: type === '_matterc._udp.local' ? 'Commissionable Matter device' : 'Matter device',
        metadata: {
          matter: true,
          mdnsName: recordArg.name,
          mdnsType: recordArg.type,
          txt: recordArg.txt,
          url: this.txt(recordArg, 'url'),
        },
      },
    };
  }

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

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

export class MatterManualMatcher implements IDiscoveryMatcher<IMatterManualEntry> {
  public id = 'matter-manual-match';
  public source = 'manual' as const;
  public description = 'Recognize manual Matter Server setup entries.';

  public async matches(inputArg: IMatterManualEntry): Promise<IDiscoveryMatch> {
    const model = inputArg.model?.toLowerCase() || '';
    const url = inputArg.url || (inputArg.host ? `ws://${inputArg.host}:${inputArg.port || 5580}/ws` : undefined);
    const matched = Boolean(url || inputArg.metadata?.matter || model.includes('matter')) || !inputArg.id && !inputArg.model;
    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'Manual entry does not contain Matter setup hints.' };
    }
    return {
      matched: true,
      confidence: url ? 'high' : 'medium',
      reason: 'Manual entry can start Matter Server setup.',
      normalizedDeviceId: inputArg.id || url || defaultMatterServerUrl,
      candidate: {
        source: 'manual',
        integrationDomain: 'matter',
        id: inputArg.id || url || defaultMatterServerUrl,
        host: inputArg.host,
        port: inputArg.port || 5580,
        name: inputArg.name || 'Matter',
        manufacturer: 'Matter',
        model: inputArg.model || 'Matter Server',
        metadata: { ...inputArg.metadata, matter: true, url: url || defaultMatterServerUrl },
      },
    };
  }
}

export class MatterCandidateValidator implements IDiscoveryValidator {
  public id = 'matter-candidate-validator';
  public description = 'Validate Matter candidate metadata.';

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

export const createMatterDiscoveryDescriptor = (): DiscoveryDescriptor => {
  return new DiscoveryDescriptor({ integrationDomain: 'matter', displayName: 'Matter' })
    .addMatcher(new MatterMdnsMatcher())
    .addMatcher(new MatterManualMatcher())
    .addValidator(new MatterCandidateValidator());
};
