import { DiscoveryDescriptor } from '../../core/classes.discoverydescriptor.js';
import type { IDiscoveryCandidate, IDiscoveryMatch, IDiscoveryMatcher, IDiscoveryValidator } from '../../core/types.js';
import type { ISonosManualEntry, ISonosMdnsRecord, ISonosSsdpRecord } from './sonos.types.js';

const sonosUpnpSt = 'urn:schemas-upnp-org:device:ZonePlayer:1';

export class SonosSsdpMatcher implements IDiscoveryMatcher<ISonosSsdpRecord> {
  public id = 'sonos-ssdp-match';
  public source = 'ssdp' as const;
  public description = 'Recognize Sonos ZonePlayer SSDP advertisements.';

  public async matches(recordArg: ISonosSsdpRecord): Promise<IDiscoveryMatch> {
    const st = recordArg.st || recordArg.headers?.st || recordArg.headers?.ST;
    const usn = recordArg.usn || recordArg.headers?.usn || recordArg.headers?.USN;
    const location = recordArg.location || recordArg.headers?.location || recordArg.headers?.LOCATION;
    const matched = st === sonosUpnpSt || Boolean(usn?.includes('RINCON_'));
    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'SSDP record is not a Sonos ZonePlayer.' };
    }
    const url = location ? new URL(location) : undefined;
    const id = usn?.match(/RINCON_[^:\s]+/)?.[0];
    return {
      matched: true,
      confidence: id ? 'certain' : 'high',
      reason: 'SSDP record matches Sonos ZonePlayer metadata.',
      normalizedDeviceId: id,
      candidate: {
        source: 'ssdp',
        integrationDomain: 'sonos',
        id,
        host: url?.hostname,
        port: url?.port ? Number(url.port) : 1400,
        manufacturer: 'Sonos',
        model: 'ZonePlayer',
        metadata: { st, usn, location },
      },
    };
  }
}

export class SonosMdnsMatcher implements IDiscoveryMatcher<ISonosMdnsRecord> {
  public id = 'sonos-mdns-match';
  public source = 'mdns' as const;
  public description = 'Recognize Sonos mDNS advertisements.';

  public async matches(recordArg: ISonosMdnsRecord): Promise<IDiscoveryMatch> {
    const type = recordArg.type?.toLowerCase() || '';
    const name = recordArg.name?.toLowerCase() || '';
    const matched = type === '_sonos._tcp.local.' || name.startsWith('sonos');
    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'mDNS record is not a Sonos advertisement.' };
    }
    const id = recordArg.txt?.id || recordArg.txt?.uid || recordArg.name;
    return {
      matched: true,
      confidence: id ? 'certain' : 'high',
      reason: 'mDNS record matches Sonos metadata.',
      normalizedDeviceId: id,
      candidate: {
        source: 'mdns',
        integrationDomain: 'sonos',
        id,
        host: recordArg.host,
        port: recordArg.port || 1400,
        manufacturer: 'Sonos',
        metadata: { mdnsName: recordArg.name, mdnsType: recordArg.type, txt: recordArg.txt },
      },
    };
  }
}

export class SonosManualMatcher implements IDiscoveryMatcher<ISonosManualEntry> {
  public id = 'sonos-manual-match';
  public source = 'manual' as const;
  public description = 'Recognize manual Sonos setup entries.';

  public async matches(inputArg: ISonosManualEntry): Promise<IDiscoveryMatch> {
    const matched = Boolean(inputArg.host || inputArg.model?.toLowerCase().includes('sonos') || inputArg.metadata?.sonos);
    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'Manual entry does not contain Sonos setup hints.' };
    }
    return {
      matched: true,
      confidence: inputArg.host ? 'high' : 'medium',
      reason: 'Manual entry can start Sonos setup.',
      normalizedDeviceId: inputArg.id,
      candidate: {
        source: 'manual',
        integrationDomain: 'sonos',
        id: inputArg.id,
        host: inputArg.host,
        port: inputArg.port || 1400,
        name: inputArg.name,
        manufacturer: 'Sonos',
        model: inputArg.model,
        metadata: inputArg.metadata,
      },
    };
  }
}

export class SonosCandidateValidator implements IDiscoveryValidator {
  public id = 'sonos-candidate-validator';
  public description = 'Validate Sonos candidate metadata.';

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

export const createSonosDiscoveryDescriptor = (): DiscoveryDescriptor => {
  return new DiscoveryDescriptor({ integrationDomain: 'sonos', displayName: 'Sonos' })
    .addMatcher(new SonosSsdpMatcher())
    .addMatcher(new SonosMdnsMatcher())
    .addMatcher(new SonosManualMatcher())
    .addValidator(new SonosCandidateValidator());
};
