import { DiscoveryDescriptor } from '../../core/classes.discoverydescriptor.js';
import type { IDiscoveryCandidate, IDiscoveryMatch, IDiscoveryMatcher, IDiscoveryValidator } from '../../core/types.js';
import type { ITradfriManualEntry, ITradfriMdnsRecord } from './tradfri.types.js';
import { tradfriDefaultCoapDtlsPort } from './tradfri.types.js';

const tradfriMdnsTypes = new Set(['_hap._tcp.local', '_homekit._tcp.local', '_tradfri._udp.local', '_coap._udp.local']);

export class TradfriMdnsMatcher implements IDiscoveryMatcher<ITradfriMdnsRecord> {
  public id = 'tradfri-mdns-match';
  public source = 'mdns' as const;
  public description = 'Recognize IKEA TRADFRI gateway mDNS and HomeKit zeroconf records.';

  public async matches(recordArg: ITradfriMdnsRecord): Promise<IDiscoveryMatch> {
    const type = this.normalizeType(recordArg.type);
    const txt = recordArg.txt || recordArg.properties || {};
    const name = recordArg.name || recordArg.hostname || '';
    const model = this.txt(txt, 'md') || this.txt(txt, 'model') || this.txt(txt, 'modelid');
    const manufacturer = this.txt(txt, 'manufacturer') || this.txt(txt, 'mfg');
    const gatewayId = this.txt(txt, 'gateway_id') || this.txt(txt, 'gatewayid') || this.txt(txt, 'id') || this.txt(txt, 'serial') || this.txt(txt, 'sn');
    const matched = tradfriMdnsTypes.has(type) && this.containsTradfri(`${name} ${model} ${manufacturer}`)
      || type === '_tradfri._udp.local'
      || this.containsTradfri(`${name} ${model} ${manufacturer}`)
      || Boolean(gatewayId && this.containsTradfri(name));

    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'mDNS record is not an IKEA TRADFRI gateway advertisement.' };
    }

    const host = recordArg.host || recordArg.hostname || recordArg.addresses?.[0];
    return {
      matched: true,
      confidence: gatewayId ? 'certain' : tradfriMdnsTypes.has(type) ? 'high' : 'medium',
      reason: 'mDNS record matches IKEA TRADFRI gateway metadata.',
      normalizedDeviceId: gatewayId || host || name,
      candidate: {
        source: 'mdns',
        integrationDomain: 'tradfri',
        id: gatewayId || host || name,
        host,
        port: type === '_hap._tcp.local' || type === '_homekit._tcp.local' ? tradfriDefaultCoapDtlsPort : recordArg.port || tradfriDefaultCoapDtlsPort,
        name: this.cleanName(name) || 'IKEA TRADFRI Gateway',
        manufacturer: 'IKEA of Sweden',
        model: model || 'TRADFRI Gateway',
        metadata: {
          mdnsName: recordArg.name,
          mdnsType: recordArg.type,
          txt,
          gatewayId,
          homekitId: this.txt(txt, 'id'),
        },
      },
    };
  }

  private txt(txtArg: Record<string, string | undefined>, keyArg: string): string | undefined {
    return txtArg[keyArg] || txtArg[keyArg.toUpperCase()];
  }

  private normalizeType(valueArg?: string): string {
    return (valueArg || '').toLowerCase().replace(/\.$/, '');
  }

  private containsTradfri(valueArg: string): boolean {
    const value = valueArg.toLowerCase();
    return value.includes('tradfri') || value.includes('tr\u00e5dfri') || value.includes('ikea');
  }

  private cleanName(valueArg: string): string | undefined {
    const cleaned = valueArg.replace(/\._[^.]+\._(?:tcp|udp)\.local\.?$/i, '').replace(/\.local\.?$/i, '').trim();
    return cleaned || undefined;
  }
}

export class TradfriManualMatcher implements IDiscoveryMatcher<ITradfriManualEntry> {
  public id = 'tradfri-manual-match';
  public source = 'manual' as const;
  public description = 'Recognize manual IKEA TRADFRI gateway host and security-code entries.';

  public async matches(inputArg: ITradfriManualEntry): Promise<IDiscoveryMatch> {
    const metadata = inputArg.metadata || {};
    const matched = Boolean(inputArg.host || inputArg.securityCode || inputArg.security_code || inputArg.psk || inputArg.key || inputArg.identity || metadata.tradfri || metadata.securityCode);
    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'Manual entry does not contain IKEA TRADFRI setup hints.' };
    }
    const gatewayId = inputArg.gatewayId || inputArg.gateway_id || inputArg.id;
    return {
      matched: true,
      confidence: inputArg.host && (inputArg.securityCode || inputArg.security_code || inputArg.psk || inputArg.key) ? 'high' : inputArg.host ? 'medium' : 'low',
      reason: 'Manual entry can start IKEA TRADFRI setup.',
      normalizedDeviceId: gatewayId || inputArg.host,
      candidate: {
        source: 'manual',
        integrationDomain: 'tradfri',
        id: gatewayId || inputArg.host,
        host: inputArg.host,
        port: inputArg.port || tradfriDefaultCoapDtlsPort,
        name: inputArg.name || 'IKEA TRADFRI Gateway',
        manufacturer: inputArg.manufacturer || 'IKEA of Sweden',
        model: inputArg.model || 'TRADFRI Gateway',
        metadata: {
          ...metadata,
          gatewayId,
          identity: inputArg.identity,
          securityCode: inputArg.securityCode || inputArg.security_code,
          psk: inputArg.psk || inputArg.key,
        },
      },
    };
  }
}

export class TradfriCandidateValidator implements IDiscoveryValidator {
  public id = 'tradfri-candidate-validator';
  public description = 'Validate IKEA TRADFRI gateway candidates before setup.';

  public async validate(candidateArg: IDiscoveryCandidate): Promise<IDiscoveryMatch> {
    const value = `${candidateArg.integrationDomain || ''} ${candidateArg.manufacturer || ''} ${candidateArg.model || ''} ${candidateArg.name || ''}`.toLowerCase();
    const metadata = candidateArg.metadata || {};
    const matched = candidateArg.integrationDomain === 'tradfri'
      || value.includes('tradfri')
      || value.includes('tr\u00e5dfri')
      || value.includes('ikea')
      || Boolean(metadata.tradfri || metadata.securityCode || metadata.psk || metadata.gatewayId);
    if (!matched || !candidateArg.host) {
      return {
        matched: false,
        confidence: matched ? 'medium' : 'low',
        reason: matched ? 'Candidate lacks a host for local TRADFRI setup.' : 'Candidate is not IKEA TRADFRI.',
        normalizedDeviceId: candidateArg.id || candidateArg.host,
      };
    }
    return {
      matched: true,
      confidence: candidateArg.id ? 'high' : 'medium',
      reason: 'Candidate has IKEA TRADFRI gateway metadata and host information.',
      candidate: {
        ...candidateArg,
        port: candidateArg.port || tradfriDefaultCoapDtlsPort,
      },
      normalizedDeviceId: candidateArg.id || candidateArg.host,
    };
  }
}

export const createTradfriDiscoveryDescriptor = (): DiscoveryDescriptor => {
  return new DiscoveryDescriptor({ integrationDomain: 'tradfri', displayName: 'IKEA TR\u00c5DFRI' })
    .addMatcher(new TradfriMdnsMatcher())
    .addMatcher(new TradfriManualMatcher())
    .addValidator(new TradfriCandidateValidator());
};
