import { DiscoveryDescriptor } from '../../core/classes.discoverydescriptor.js';
import type { IDiscoveryCandidate, IDiscoveryMatch, IDiscoveryMatcher, IDiscoveryValidator } from '../../core/types.js';
import type { IApcupsdManualEntry, IApcupsdSnapshot } from './apcupsd.types.js';
import { apcupsdDefaultPort } from './apcupsd.types.js';

export class ApcupsdManualMatcher implements IDiscoveryMatcher<IApcupsdManualEntry> {
  public id = 'apcupsd-manual-match';
  public source = 'manual' as const;
  public description = 'Recognize manual APCUPSd NIS setup entries.';

  public async matches(inputArg: IApcupsdManualEntry): Promise<IDiscoveryMatch> {
    const haystack = `${inputArg.name || ''} ${inputArg.manufacturer || ''} ${inputArg.model || ''}`.toLowerCase();
    const hasSnapshot = Boolean(inputArg.snapshot || inputArg.rawStatus || inputArg.metadata?.snapshot || inputArg.metadata?.rawStatus);
    const matched = Boolean(inputArg.host || inputArg.port === apcupsdDefaultPort || inputArg.metadata?.apcupsd || hasSnapshot || haystack.includes('apcupsd') || haystack.includes('apc ups') || haystack.includes('ups daemon'));
    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'Manual entry does not contain APCUPSd setup hints.' };
    }

    const port = inputArg.port || apcupsdDefaultPort;
    const serialNumber = this.snapshotSerial(inputArg.snapshot || inputArg.metadata?.snapshot as IApcupsdSnapshot | undefined);
    const id = inputArg.id || serialNumber || (inputArg.host ? `${inputArg.host}:${port}` : undefined);
    return {
      matched: true,
      confidence: inputArg.host || hasSnapshot ? 'high' : 'medium',
      reason: 'Manual entry can start APCUPSd setup.',
      normalizedDeviceId: id,
      candidate: {
        source: 'manual',
        integrationDomain: 'apcupsd',
        id,
        host: inputArg.host,
        port,
        name: inputArg.name,
        manufacturer: inputArg.manufacturer || 'APC',
        model: inputArg.model || 'UPS',
        metadata: {
          ...inputArg.metadata,
          apcupsd: true,
          hasSnapshot,
          serialNumber,
        },
      },
    };
  }

  private snapshotSerial(snapshotArg: IApcupsdSnapshot | undefined): string | undefined {
    return snapshotArg?.ups.serialNumber || snapshotArg?.status.SERIALNO;
  }
}

export class ApcupsdCandidateValidator implements IDiscoveryValidator {
  public id = 'apcupsd-candidate-validator';
  public description = 'Validate APCUPSd candidates have manual endpoint or snapshot information.';

  public async validate(candidateArg: IDiscoveryCandidate): Promise<IDiscoveryMatch> {
    const haystack = `${candidateArg.name || ''} ${candidateArg.manufacturer || ''} ${candidateArg.model || ''}`.toLowerCase();
    const matched = candidateArg.integrationDomain === 'apcupsd'
      || candidateArg.port === apcupsdDefaultPort
      || Boolean(candidateArg.metadata?.apcupsd)
      || haystack.includes('apcupsd')
      || haystack.includes('apc ups')
      || haystack.includes('ups daemon');
    const hasUsableSource = Boolean(candidateArg.host || candidateArg.metadata?.hasSnapshot || candidateArg.metadata?.rawStatus || candidateArg.metadata?.snapshot);

    if (!matched || !hasUsableSource) {
      return {
        matched: false,
        confidence: matched ? 'medium' : 'low',
        reason: matched ? 'APCUPSd candidate lacks host or snapshot information.' : 'Candidate is not APCUPSd.',
      };
    }

    const port = candidateArg.port || apcupsdDefaultPort;
    const serialNumber = typeof candidateArg.metadata?.serialNumber === 'string' ? candidateArg.metadata.serialNumber : undefined;
    const normalizedDeviceId = candidateArg.id || serialNumber || (candidateArg.host ? `${candidateArg.host}:${port}` : undefined);
    return {
      matched: true,
      confidence: candidateArg.id || serialNumber ? 'certain' : 'high',
      reason: 'Candidate has APCUPSd metadata and a usable manual source.',
      normalizedDeviceId,
      candidate: {
        ...candidateArg,
        port,
      },
    };
  }
}

export const createApcupsdDiscoveryDescriptor = (): DiscoveryDescriptor => {
  return new DiscoveryDescriptor({ integrationDomain: 'apcupsd', displayName: 'APC UPS Daemon' })
    .addMatcher(new ApcupsdManualMatcher())
    .addValidator(new ApcupsdCandidateValidator());
};
