import { DiscoveryDescriptor } from '../../core/classes.discoverydescriptor.js';
import type { IDiscoveryCandidate, IDiscoveryMatch, IDiscoveryMatcher, IDiscoveryValidator } from '../../core/types.js';
import type { IBoschShcManualEntry, IBoschShcMdnsRecord } from './bosch_shc.types.js';
import { formatMac } from './bosch_shc.mapper.js';

const boschHttpType = '_http._tcp.local';

export class BoschShcMdnsMatcher implements IDiscoveryMatcher<IBoschShcMdnsRecord> {
  public id = 'bosch-shc-mdns-match';
  public source = 'mdns' as const;
  public description = 'Recognize Bosch Smart Home Controller zeroconf records named Bosch SHC on _http._tcp.local.';

  public async matches(recordArg: IBoschShcMdnsRecord): Promise<IDiscoveryMatch> {
    const type = normalizeMdnsType(recordArg.type);
    const name = recordArg.name || '';
    const isBoschShc = type === boschHttpType && name.toLowerCase().startsWith('bosch shc');
    if (!isBoschShc) {
      return { matched: false, confidence: 'low', reason: 'mDNS record is not a Bosch SHC _http._tcp.local advertisement.' };
    }

    const macAddress = formatMac(extractMac(name) || stringValue(recordArg.txt?.mac) || stringValue(recordArg.txt?.id));
    const id = macAddress || recordArg.name;
    const host = recordArg.host || recordArg.hostname?.replace(/\.$/, '');
    return {
      matched: true,
      confidence: 'certain',
      reason: 'mDNS record matches Home Assistant Bosch SHC zeroconf criteria.',
      normalizedDeviceId: id,
      candidate: {
        source: 'mdns',
        integrationDomain: 'bosch_shc',
        id,
        host,
        port: recordArg.port,
        name: nodeName(recordArg.hostname || recordArg.name),
        manufacturer: 'Bosch',
        model: 'Smart Home Controller',
        macAddress,
        metadata: {
          mdnsName: recordArg.name,
          mdnsType: recordArg.type,
          txt: recordArg.txt,
          bosch_shc: true,
        },
      },
    };
  }
}

export class BoschShcManualMatcher implements IDiscoveryMatcher<IBoschShcManualEntry> {
  public id = 'bosch-shc-manual-match';
  public source = 'manual' as const;
  public description = 'Recognize manual Bosch SHC setup entries by selected integration, host, or Bosch metadata.';

  public async matches(inputArg: IBoschShcManualEntry): Promise<IDiscoveryMatch> {
    const manufacturer = inputArg.manufacturer?.toLowerCase() || '';
    const model = inputArg.model?.toLowerCase() || '';
    const matched = Boolean(inputArg.host || manufacturer === 'bosch' && model.includes('smart home') || inputArg.metadata?.bosch_shc);
    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'Manual entry does not contain Bosch SHC setup hints.' };
    }
    const id = formatMac(inputArg.macAddress || inputArg.id) || inputArg.id;
    return {
      matched: true,
      confidence: inputArg.host ? 'high' : 'medium',
      reason: 'Manual entry can start Bosch SHC host setup.',
      normalizedDeviceId: id,
      candidate: {
        source: 'manual',
        integrationDomain: 'bosch_shc',
        id,
        host: inputArg.host,
        port: inputArg.port,
        name: inputArg.name,
        manufacturer: 'Bosch',
        model: inputArg.model || 'Smart Home Controller',
        macAddress: formatMac(inputArg.macAddress),
        metadata: { ...inputArg.metadata, bosch_shc: true },
      },
    };
  }
}

export class BoschShcCandidateValidator implements IDiscoveryValidator {
  public id = 'bosch-shc-candidate-validator';
  public description = 'Validate Bosch SHC candidates before starting local controller setup.';

  public async validate(candidateArg: IDiscoveryCandidate): Promise<IDiscoveryMatch> {
    const manufacturer = candidateArg.manufacturer?.toLowerCase() || '';
    const model = candidateArg.model?.toLowerCase() || '';
    const matched = candidateArg.integrationDomain === 'bosch_shc'
      || Boolean(candidateArg.metadata?.bosch_shc)
      || manufacturer === 'bosch' && (model.includes('smart home') || model.includes('shc'));
    const hasHost = Boolean(candidateArg.host);
    return {
      matched: matched && hasHost,
      confidence: matched && candidateArg.id ? 'certain' : matched && hasHost ? 'high' : matched ? 'medium' : 'low',
      reason: matched
        ? hasHost ? 'Candidate has Bosch SHC metadata and a usable local host.' : 'Candidate has Bosch SHC metadata but no local host.'
        : 'Candidate is not Bosch SHC.',
      candidate: matched && hasHost ? candidateArg : undefined,
      normalizedDeviceId: formatMac(candidateArg.macAddress || candidateArg.id) || candidateArg.id,
    };
  }
}

export const createBoschShcDiscoveryDescriptor = (): DiscoveryDescriptor => {
  return new DiscoveryDescriptor({
    integrationDomain: 'bosch_shc',
    displayName: 'Bosch SHC',
  })
    .addMatcher(new BoschShcMdnsMatcher())
    .addMatcher(new BoschShcManualMatcher())
    .addValidator(new BoschShcCandidateValidator());
};

const normalizeMdnsType = (valueArg?: string): string => (valueArg || '').toLowerCase().replace(/\.$/, '');

const extractMac = (valueArg: string): string | undefined => {
  const match = /\[([^\]]+)\]/.exec(valueArg);
  return match?.[1];
};

const nodeName = (valueArg?: string): string | undefined => {
  const value = valueArg?.replace(/\.$/, '').replace(/\._http\._tcp\.local$/i, '').replace(/\.local$/i, '').trim();
  return value || undefined;
};

const stringValue = (valueArg: unknown): string | undefined => {
  return typeof valueArg === 'string' && valueArg.trim() ? valueArg.trim() : undefined;
};
