import { DiscoveryDescriptor } from '../../core/classes.discoverydescriptor.js';
import type { IDiscoveryCandidate, IDiscoveryMatch, IDiscoveryMatcher, IDiscoveryValidator } from '../../core/types.js';
import type { IMqttDiscoveryMessage, IMqttManualEntry } from './mqtt.types.js';

const defaultDiscoveryPrefix = 'homeassistant';

export class MqttManualMatcher implements IDiscoveryMatcher<IMqttManualEntry> {
  public id = 'mqtt-manual-match';
  public source = 'manual' as const;
  public description = 'Recognize manual MQTT broker setup entries.';

  public async matches(inputArg: IMqttManualEntry): Promise<IDiscoveryMatch> {
    const model = inputArg.model?.toLowerCase() || '';
    const matched = Boolean(inputArg.host || inputArg.metadata?.mqtt || model.includes('mqtt') || model.includes('mosquitto'));
    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'Manual entry does not contain MQTT broker setup hints.' };
    }
    return {
      matched: true,
      confidence: inputArg.host ? 'high' : 'medium',
      reason: 'Manual entry can start MQTT broker setup.',
      normalizedDeviceId: inputArg.id || inputArg.host,
      candidate: {
        source: 'manual',
        integrationDomain: 'mqtt',
        id: inputArg.id || inputArg.host,
        host: inputArg.host,
        port: inputArg.port || (inputArg.protocol === 'mqtts' ? 8883 : 1883),
        name: inputArg.name || 'MQTT broker',
        manufacturer: 'MQTT',
        model: inputArg.model || 'Broker',
        metadata: {
          ...inputArg.metadata,
          protocol: inputArg.protocol || 'mqtt',
          username: inputArg.username,
        },
      },
    };
  }
}

export class MqttDiscoveryTopicMatcher implements IDiscoveryMatcher<IMqttDiscoveryMessage> {
  public id = 'mqtt-discovery-topic-match';
  public source = 'mqtt' as const;
  public description = 'Recognize Home Assistant MQTT discovery config topics.';

  public async matches(inputArg: IMqttDiscoveryMessage): Promise<IDiscoveryMatch> {
    const topic = inputArg.topic || '';
    const match = this.discoveryMatch(topic);
    if (!match) {
      return { matched: false, confidence: 'low', reason: 'MQTT message is not on a discovery config topic.' };
    }
    return {
      matched: true,
      confidence: 'certain',
      reason: 'MQTT topic matches Home Assistant discovery format.',
      normalizedDeviceId: match.objectId,
      candidate: {
        source: 'mqtt',
        integrationDomain: 'mqtt',
        id: `${match.component}:${match.nodeId || ''}:${match.objectId}`,
        name: match.objectId,
        metadata: {
          discoveryPrefix: match.prefix,
          component: match.component,
          nodeId: match.nodeId,
          objectId: match.objectId,
          topic,
          payload: inputArg.payload,
        },
      },
    };
  }

  private discoveryMatch(topicArg: string): { prefix: string; component: string; nodeId?: string; objectId: string } | undefined {
    const parts = topicArg.split('/').filter(Boolean);
    if (parts.length < 4 || parts[parts.length - 1] !== 'config') {
      return undefined;
    }
    const prefix = parts[0] || defaultDiscoveryPrefix;
    const component = parts[1];
    if (parts.length === 4) {
      return { prefix, component, objectId: parts[2] };
    }
    if (parts.length === 5) {
      return { prefix, component, nodeId: parts[2], objectId: parts[3] };
    }
    return undefined;
  }
}

export class MqttCandidateValidator implements IDiscoveryValidator {
  public id = 'mqtt-candidate-validator';
  public description = 'Validate MQTT broker or discovery candidate metadata.';

  public async validate(candidateArg: IDiscoveryCandidate): Promise<IDiscoveryMatch> {
    const metadataProtocol = candidateArg.metadata?.protocol;
    const matched = candidateArg.integrationDomain === 'mqtt' || candidateArg.port === 1883 || candidateArg.port === 8883 || candidateArg.metadata?.mqtt === true || metadataProtocol === 'mqtt' || metadataProtocol === 'mqtts';
    return {
      matched,
      confidence: matched && candidateArg.host ? 'high' : matched ? 'medium' : 'low',
      reason: matched ? 'Candidate has MQTT broker metadata.' : 'Candidate is not MQTT.',
      candidate: matched ? candidateArg : undefined,
      normalizedDeviceId: candidateArg.id || candidateArg.host,
    };
  }
}

export const createMqttDiscoveryDescriptor = (): DiscoveryDescriptor => {
  return new DiscoveryDescriptor({ integrationDomain: 'mqtt', displayName: 'MQTT' })
    .addMatcher(new MqttManualMatcher())
    .addMatcher(new MqttDiscoveryTopicMatcher())
    .addValidator(new MqttCandidateValidator());
};
