import { DiscoveryDescriptor } from '../../core/classes.discoverydescriptor.js';
import type { IDiscoveryCandidate, IDiscoveryMatch, IDiscoveryMatcher, IDiscoveryValidator } from '../../core/types.js';
import { homematicDefaultInterfacePort, type IHomematicManualEntry, type IHomematicMetadataDiscoveryRecord } from './homematic.types.js';

export class HomematicManualMatcher implements IDiscoveryMatcher<IHomematicManualEntry> {
  public id = 'homematic-manual-match';
  public source = 'manual' as const;
  public description = 'Recognize manual Homematic CCU/Homegear XML-RPC setup entries.';

  public async matches(inputArg: IHomematicManualEntry): Promise<IDiscoveryMatch> {
    const haystack = `${inputArg.name || ''} ${inputArg.model || ''} ${inputArg.manufacturer || ''} ${inputArg.metadata?.domain || ''}`.toLowerCase();
    const matched = Boolean(
      inputArg.metadata?.homematic
      || inputArg.metadata?.homeMatic
      || inputArg.metadata?.ccu
      || inputArg.metadata?.domain === 'homematic'
      || haystack.includes('homematic')
      || haystack.includes('homegear')
      || haystack.includes('ccu')
      || haystack.includes('eq-3')
    );

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

    const id = normalizeId(inputArg.id || inputArg.serialNumber || inputArg.macAddress || inputArg.host || inputArg.name || 'homematic');
    return {
      matched: true,
      confidence: inputArg.host ? 'high' : 'medium',
      reason: 'Manual entry can start Homematic XML-RPC setup.',
      normalizedDeviceId: id,
      candidate: {
        source: 'manual',
        integrationDomain: 'homematic',
        id,
        host: inputArg.host,
        port: inputArg.port || homematicDefaultInterfacePort,
        name: inputArg.name || 'Homematic CCU',
        manufacturer: inputArg.manufacturer || 'eQ-3',
        model: inputArg.model || 'Homematic CCU',
        serialNumber: inputArg.serialNumber,
        macAddress: inputArg.macAddress,
        metadata: {
          ...inputArg.metadata,
          interfaceName: inputArg.interfaceName || inputArg.metadata?.interfaceName || 'default',
          ssl: inputArg.ssl,
          path: inputArg.path,
        },
      },
    };
  }
}

export class HomematicMetadataMatcher implements IDiscoveryMatcher<IHomematicMetadataDiscoveryRecord> {
  public id = 'homematic-metadata-match';
  public source = 'custom' as const;
  public description = 'Recognize metadata records from Home Assistant/HomeMatic manifests or inventories.';

  public async matches(inputArg: IHomematicMetadataDiscoveryRecord): Promise<IDiscoveryMatch> {
    const metadata = inputArg.metadata || {};
    const domain = inputArg.domain || inputArg.integrationDomain || metadata.domain || metadata.integrationDomain || metadata.upstreamDomain;
    const haystack = `${inputArg.name || ''} ${inputArg.model || ''} ${inputArg.manufacturer || ''} ${domain || ''}`.toLowerCase();
    const matched = domain === 'homematic' || Boolean(metadata.homematic || metadata.homeMatic || haystack.includes('homematic'));
    return {
      matched,
      confidence: matched && inputArg.host ? 'high' : matched ? 'medium' : 'low',
      reason: matched ? 'Metadata identifies Homematic.' : 'Metadata is not Homematic.',
      normalizedDeviceId: matched ? normalizeId(String(inputArg.id || inputArg.host || inputArg.name || 'homematic')) : undefined,
      candidate: matched ? {
        source: 'custom',
        integrationDomain: 'homematic',
        id: normalizeId(String(inputArg.id || inputArg.host || inputArg.name || 'homematic')),
        host: inputArg.host,
        port: inputArg.port || homematicDefaultInterfacePort,
        name: inputArg.name || 'Homematic CCU',
        manufacturer: inputArg.manufacturer || 'eQ-3',
        model: inputArg.model || 'Homematic CCU',
        metadata,
      } : undefined,
    };
  }
}

export class HomematicCandidateValidator implements IDiscoveryValidator {
  public id = 'homematic-candidate-validator';
  public description = 'Validate that a discovery candidate can be configured as a Homematic CCU/Homegear endpoint.';

  public async validate(candidateArg: IDiscoveryCandidate): Promise<IDiscoveryMatch> {
    const metadata = candidateArg.metadata || {};
    const haystack = `${candidateArg.name || ''} ${candidateArg.model || ''} ${candidateArg.manufacturer || ''} ${candidateArg.integrationDomain || ''}`.toLowerCase();
    const matched = candidateArg.integrationDomain === 'homematic'
      || Boolean(metadata.homematic || metadata.homeMatic || metadata.ccu)
      || haystack.includes('homematic')
      || haystack.includes('homegear')
      || haystack.includes('ccu')
      || haystack.includes('eq-3');
    const id = normalizeId(candidateArg.id || candidateArg.serialNumber || candidateArg.macAddress || candidateArg.host || candidateArg.name || 'homematic');
    return {
      matched,
      confidence: matched && candidateArg.host ? 'high' : matched ? 'medium' : 'low',
      reason: matched ? 'Candidate has Homematic metadata.' : 'Candidate is not Homematic.',
      normalizedDeviceId: id,
      candidate: matched ? {
        ...candidateArg,
        integrationDomain: 'homematic',
        id,
        manufacturer: candidateArg.manufacturer || 'eQ-3',
        model: candidateArg.model || 'Homematic CCU',
        port: candidateArg.port || homematicDefaultInterfacePort,
      } : undefined,
    };
  }
}

export const createHomematicDiscoveryDescriptor = (): DiscoveryDescriptor => {
  return new DiscoveryDescriptor({ integrationDomain: 'homematic', displayName: 'Homematic' })
    .addMatcher(new HomematicManualMatcher())
    .addMatcher(new HomematicMetadataMatcher())
    .addValidator(new HomematicCandidateValidator());
};

const normalizeId = (valueArg: string | undefined): string => {
  const value = (valueArg || 'homematic').toLowerCase().replace(/[^a-z0-9]+/g, '_').replace(/^_+|_+$/g, '');
  return value || 'homematic';
};
