import { DiscoveryDescriptor } from '../../core/classes.discoverydescriptor.js';
import type { IDiscoveryCandidate, IDiscoveryMatch, IDiscoveryMatcher, IDiscoveryValidator } from '../../core/types.js';
import { rflinkDefaultBaudRate, rflinkDomain } from './rflink.constants.js';
import type { IRflinkCandidateMetadata, IRflinkManualEntry } from './rflink.types.js';

export class RflinkManualMatcher implements IDiscoveryMatcher<IRflinkManualEntry> {
  public id = 'rflink-manual-match';
  public source = 'manual' as const;
  public description = 'Recognize manual RFLink serial and TCP gateway entries.';

  public async matches(inputArg: IRflinkManualEntry): Promise<IDiscoveryMatch> {
    const metadata = inputArg.metadata || {};
    const connectionType = this.connectionType(inputArg);
    const serialPort = this.serialPort(inputArg);
    const host = 'host' in inputArg ? inputArg.host : undefined;
    const tcpPort = typeof inputArg.port === 'number' ? inputArg.port : undefined;
    const baudRate = 'baudRate' in inputArg ? inputArg.baudRate : undefined;
    const haystack = `${inputArg.name || ''} ${inputArg.manufacturer || ''} ${inputArg.model || ''} ${metadata.protocol || ''} ${metadata.service || ''}`.toLowerCase();
    const hasHint = Boolean(metadata.rflink || metadata.rfLink || metadata.protocol === 'rflink' || metadata.service === 'rflink' || haystack.includes('rflink') || haystack.includes('rf link'));
    const matched = Boolean(hasHint || connectionType === 'serial' && serialPort || connectionType === 'tcp' && host && tcpPort);
    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'Manual entry does not contain RFLink setup hints.' };
    }

    const candidateMetadata: IRflinkCandidateMetadata = {
      ...metadata,
      rflink: true,
      connectionType,
      serialPort,
      baudRate: baudRate || rflinkDefaultBaudRate,
    };

    return {
      matched: true,
      confidence: hasHint ? 'high' : 'medium',
      reason: connectionType === 'tcp' ? 'Manual entry can start RFLink TCP setup.' : 'Manual entry can start RFLink serial setup.',
      normalizedDeviceId: inputArg.id || host || serialPort,
      candidate: {
        source: 'manual',
        integrationDomain: rflinkDomain,
        id: inputArg.id || host || serialPort,
        host,
        port: tcpPort,
        name: inputArg.name || 'RFLink Gateway',
        manufacturer: inputArg.manufacturer || 'Nodo',
        model: inputArg.model || 'RFLink Gateway',
        metadata: candidateMetadata,
      },
    };
  }

  private connectionType(inputArg: IRflinkManualEntry): 'serial' | 'tcp' {
    if (inputArg.connectionType === 'tcp' || 'host' in inputArg && inputArg.host) {
      return 'tcp';
    }
    return 'serial';
  }

  private serialPort(inputArg: IRflinkManualEntry): string | undefined {
    if ('path' in inputArg && typeof inputArg.path === 'string' && inputArg.path.trim()) {
      return inputArg.path.trim();
    }
    return typeof inputArg.port === 'string' && inputArg.port.trim() ? inputArg.port.trim() : undefined;
  }
}

export class RflinkCandidateValidator implements IDiscoveryValidator {
  public id = 'rflink-candidate-validator';
  public description = 'Validate that a discovery candidate can configure an RFLink gateway.';

  public async validate(candidateArg: IDiscoveryCandidate): Promise<IDiscoveryMatch> {
    const metadata = candidateArg.metadata || {};
    const connectionType = metadata.connectionType === 'tcp' || candidateArg.host ? 'tcp' : metadata.connectionType === 'serial' || metadata.serialPort ? 'serial' : undefined;
    const haystack = `${candidateArg.name || ''} ${candidateArg.manufacturer || ''} ${candidateArg.model || ''} ${metadata.protocol || ''} ${metadata.service || ''}`.toLowerCase();
    const matched = Boolean(candidateArg.integrationDomain === rflinkDomain || metadata.rflink || metadata.rfLink || haystack.includes('rflink') || haystack.includes('rf link') || connectionType === 'tcp' && candidateArg.host && candidateArg.port || connectionType === 'serial' && metadata.serialPort);
    return {
      matched,
      confidence: matched && (candidateArg.host || metadata.serialPort) ? 'high' : matched ? 'medium' : 'low',
      reason: matched ? 'Candidate has RFLink gateway metadata.' : 'Candidate is not an RFLink gateway.',
      normalizedDeviceId: candidateArg.id || candidateArg.host || String(metadata.serialPort || ''),
      candidate: matched ? {
        ...candidateArg,
        integrationDomain: rflinkDomain,
        manufacturer: candidateArg.manufacturer || 'Nodo',
        model: candidateArg.model || 'RFLink Gateway',
        metadata: {
          ...metadata,
          rflink: true,
          connectionType,
        },
      } : undefined,
    };
  }
}

export const createRflinkDiscoveryDescriptor = (): DiscoveryDescriptor => {
  return new DiscoveryDescriptor({ integrationDomain: rflinkDomain, displayName: 'RFLink' })
    .addMatcher(new RflinkManualMatcher())
    .addValidator(new RflinkCandidateValidator());
};
