import { DiscoveryDescriptor } from '../../core/classes.discoverydescriptor.js';
import type { IDiscoveryCandidate, IDiscoveryMatch, IDiscoveryMatcher, IDiscoveryValidator } from '../../core/types.js';
import type { IShellyManualEntry, IShellyMdnsRecord } from './shelly.types.js';

export class ShellyMdnsMatcher implements IDiscoveryMatcher<IShellyMdnsRecord> {
  public id = 'shelly-mdns-match';
  public source = 'mdns' as const;
  public description = 'Recognize Shelly zeroconf records advertised as shelly* or _shelly._tcp.local.';

  public async matches(recordArg: IShellyMdnsRecord): Promise<IDiscoveryMatch> {
    const name = recordArg.name?.toLowerCase() || '';
    const type = recordArg.type?.toLowerCase() || '';
    const matched = name.startsWith('shelly') || type === '_shelly._tcp.local.' || type === '_http._tcp.local.' && name.startsWith('shelly');
    if (!matched) {
      return {
        matched: false,
        confidence: 'low',
        reason: 'mDNS record is not a Shelly advertisement.',
      };
    }
    const deviceId = recordArg.txt?.id || recordArg.txt?.mac || recordArg.name;
    return {
      matched: true,
      confidence: deviceId ? 'certain' : 'high',
      reason: 'mDNS record matches Shelly zeroconf metadata.',
      normalizedDeviceId: deviceId,
      candidate: {
        source: 'mdns',
        integrationDomain: 'shelly',
        id: deviceId,
        host: recordArg.host,
        port: recordArg.port || 80,
        manufacturer: 'Shelly',
        model: recordArg.txt?.model,
        metadata: {
          mdnsName: recordArg.name,
          mdnsType: recordArg.type,
          txt: recordArg.txt,
        },
      },
    };
  }
}

export class ShellyManualMatcher implements IDiscoveryMatcher<IShellyManualEntry> {
  public id = 'shelly-manual-match';
  public source = 'manual' as const;
  public description = 'Recognize manual Shelly setup entries by host or Shelly metadata.';

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

export class ShellyCandidateValidator implements IDiscoveryValidator {
  public id = 'shelly-candidate-validator';
  public description = 'Validate candidate metadata before starting Shelly setup.';

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

export const createShellyDiscoveryDescriptor = (): DiscoveryDescriptor => {
  return new DiscoveryDescriptor({
    integrationDomain: 'shelly',
    displayName: 'Shelly',
  })
    .addMatcher(new ShellyMdnsMatcher())
    .addMatcher(new ShellyManualMatcher())
    .addValidator(new ShellyCandidateValidator());
};
