import { DiscoveryDescriptor } from '../../core/classes.discoverydescriptor.js';
import type { IDiscoveryCandidate, IDiscoveryMatch, IDiscoveryMatcher, IDiscoveryValidator } from '../../core/types.js';
import { AsuswrtMapper } from './asuswrt.mapper.js';
import type { IAsuswrtManualDiscoveryRecord, IAsuswrtSnapshot, TAsuswrtProtocol } from './asuswrt.types.js';
import { asuswrtDomain } from './asuswrt.types.js';

const asusTextHints = ['asuswrt', 'asus router', 'asus wireless', 'rt-', 'gt-', 'zenwifi', 'aimesh', 'rog rapture'];

export class AsuswrtManualMatcher implements IDiscoveryMatcher<IAsuswrtManualDiscoveryRecord> {
  public id = 'asuswrt-manual-match';
  public source = 'manual' as const;
  public description = 'Recognize manual ASUSWRT router setup entries, including snapshot-only records.';

  public async matches(inputArg: IAsuswrtManualDiscoveryRecord): Promise<IDiscoveryMatch> {
    const metadata = inputArg.metadata || {};
    const snapshot = inputArg.snapshot || metadata.snapshot as IAsuswrtSnapshot | undefined;
    const host = inputArg.host || snapshot?.router.host;
    const protocol = this.protocol(inputArg.protocol || metadata.protocol || snapshot?.router.protocol) || 'https';
    const mac = AsuswrtMapper.normalizeMac(inputArg.macAddress || snapshot?.router.labelMac || snapshot?.router.macAddress);
    const text = [inputArg.integrationDomain, inputArg.manufacturer, inputArg.model, inputArg.name, metadata.manufacturer, metadata.model, metadata.name, snapshot?.router.manufacturer, snapshot?.router.model, snapshot?.router.name]
      .filter((valueArg): valueArg is string => typeof valueArg === 'string')
      .join(' ')
      .toLowerCase();
    const hasSnapshot = Boolean(snapshot);
    const matched = inputArg.integrationDomain === asuswrtDomain
      || metadata.asuswrt === true
      || hasSnapshot
      || asusTextHints.some((hintArg) => text.includes(hintArg))
      || Boolean(host && !text);

    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'Manual entry does not contain ASUSWRT setup hints.' };
    }

    const port = inputArg.port || snapshot?.router.port || AsuswrtMapper.defaultPort(protocol);
    const id = inputArg.id || mac || snapshot?.router.id || (host ? `${host}:${port}` : undefined);
    return {
      matched: true,
      confidence: hasSnapshot || mac ? 'certain' : host ? 'high' : 'medium',
      reason: hasSnapshot ? 'Manual entry includes an ASUSWRT snapshot.' : 'Manual entry can start ASUSWRT router setup.',
      normalizedDeviceId: id,
      candidate: {
        source: 'manual',
        integrationDomain: asuswrtDomain,
        id,
        host,
        port,
        name: inputArg.name || snapshot?.router.name || host || 'ASUSWRT',
        manufacturer: inputArg.manufacturer || snapshot?.router.manufacturer || 'Asus',
        model: inputArg.model || snapshot?.router.model || 'Asus Router',
        macAddress: mac,
        metadata: {
          ...metadata,
          asuswrt: true,
          protocol,
          mode: inputArg.mode || snapshot?.router.mode || 'router',
          hasSnapshot,
          liveSshTelnetImplemented: false,
        },
      },
      metadata: { hasSnapshot, protocol, liveSshTelnetImplemented: false },
    };
  }

  private protocol(valueArg: unknown): TAsuswrtProtocol | undefined {
    return valueArg === 'http' || valueArg === 'https' || valueArg === 'ssh' || valueArg === 'telnet' ? valueArg : undefined;
  }
}

export class AsuswrtCandidateValidator implements IDiscoveryValidator {
  public id = 'asuswrt-candidate-validator';
  public description = 'Validate ASUSWRT manual candidates have a host or snapshot and router metadata.';

  public async validate(candidateArg: IDiscoveryCandidate): Promise<IDiscoveryMatch> {
    const metadata = candidateArg.metadata || {};
    const snapshot = metadata.snapshot as IAsuswrtSnapshot | undefined;
    const mac = AsuswrtMapper.normalizeMac(candidateArg.macAddress || snapshot?.router.labelMac || snapshot?.router.macAddress);
    const text = [candidateArg.integrationDomain, candidateArg.manufacturer, candidateArg.model, candidateArg.name, metadata.manufacturer, metadata.model, metadata.name]
      .filter((valueArg): valueArg is string => typeof valueArg === 'string')
      .join(' ')
      .toLowerCase();
    const matched = candidateArg.integrationDomain === asuswrtDomain
      || metadata.asuswrt === true
      || Boolean(snapshot)
      || asusTextHints.some((hintArg) => text.includes(hintArg))
      || candidateArg.source === 'manual' && Boolean(candidateArg.host);
    const hasUsableSource = Boolean(candidateArg.host || snapshot);

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

    const protocol = this.protocol(metadata.protocol) || snapshot?.router.protocol || 'https';
    const port = candidateArg.port || snapshot?.router.port || AsuswrtMapper.defaultPort(protocol);
    const normalizedDeviceId = candidateArg.id || mac || (candidateArg.host ? `${candidateArg.host}:${port}` : snapshot?.router.id);
    return {
      matched: true,
      confidence: mac || snapshot ? 'certain' : candidateArg.host ? 'high' : 'medium',
      reason: 'Candidate has ASUSWRT metadata and a usable manual source.',
      normalizedDeviceId,
      candidate: {
        ...candidateArg,
        id: candidateArg.id || normalizedDeviceId,
        port,
        macAddress: mac || candidateArg.macAddress,
      },
      metadata: { protocol, liveSshTelnetImplemented: false },
    };
  }

  private protocol(valueArg: unknown): TAsuswrtProtocol | undefined {
    return valueArg === 'http' || valueArg === 'https' || valueArg === 'ssh' || valueArg === 'telnet' ? valueArg : undefined;
  }
}

export const createAsuswrtDiscoveryDescriptor = (): DiscoveryDescriptor => {
  return new DiscoveryDescriptor({ integrationDomain: asuswrtDomain, displayName: 'ASUSWRT' })
    .addMatcher(new AsuswrtManualMatcher())
    .addValidator(new AsuswrtCandidateValidator());
};
