import { DiscoveryDescriptor } from '../../core/classes.discoverydescriptor.js';
import type { IDiscoveryCandidate, IDiscoveryMatch, IDiscoveryMatcher, IDiscoveryValidator } from '../../core/types.js';
import type { IRokuManualEntry, IRokuSsdpRecord } from './roku.types.js';

export class RokuSsdpMatcher implements IDiscoveryMatcher<IRokuSsdpRecord> {
  public id = 'roku-ssdp-match';
  public source = 'ssdp' as const;
  public description = 'Recognize Roku ECP SSDP advertisements.';

  public async matches(recordArg: IRokuSsdpRecord): 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 manufacturer = recordArg.headers?.manufacturer || recordArg.headers?.MANUFACTURER;
    const matched = st === 'roku:ecp' || manufacturer === 'Roku' || Boolean(usn?.toLowerCase().includes('roku'));
    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'SSDP record is not Roku ECP.' };
    }
    const url = location ? new URL(location) : undefined;
    const id = usn?.replace(/^uuid:/i, '').split('::')[0];
    return {
      matched: true,
      confidence: id ? 'certain' : 'high',
      reason: 'SSDP record matches Roku ECP metadata.',
      normalizedDeviceId: id,
      candidate: {
        source: 'ssdp',
        integrationDomain: 'roku',
        id,
        host: url?.hostname,
        port: url?.port ? Number(url.port) : 8060,
        manufacturer: 'Roku',
        model: recordArg.headers?.modelName || recordArg.headers?.MODELNAME,
        metadata: { st, usn, location },
      },
    };
  }
}

export class RokuManualMatcher implements IDiscoveryMatcher<IRokuManualEntry> {
  public id = 'roku-manual-match';
  public source = 'manual' as const;
  public description = 'Recognize manual Roku setup entries.';

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

export class RokuCandidateValidator implements IDiscoveryValidator {
  public id = 'roku-candidate-validator';
  public description = 'Validate Roku ECP candidate metadata.';

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

export const createRokuDiscoveryDescriptor = (): DiscoveryDescriptor => {
  return new DiscoveryDescriptor({ integrationDomain: 'roku', displayName: 'Roku' })
    .addMatcher(new RokuSsdpMatcher())
    .addMatcher(new RokuManualMatcher())
    .addValidator(new RokuCandidateValidator());
};
