import { DiscoveryDescriptor } from '../../core/classes.discoverydescriptor.js';
import type { IDiscoveryCandidate, IDiscoveryMatch, IDiscoveryMatcher, IDiscoveryValidator } from '../../core/types.js';
import type { IVolumioManualEntry, IVolumioMdnsRecord } from './volumio.types.js';
import { volumioDefaultPort } from './volumio.types.js';

const volumioDomain = 'volumio';
const volumioMdnsType = '_volumio._tcp.local';

export class VolumioMdnsMatcher implements IDiscoveryMatcher<IVolumioMdnsRecord> {
  public id = 'volumio-mdns-match';
  public source = 'mdns' as const;
  public description = 'Recognize Volumio zeroconf advertisements.';

  public async matches(recordArg: IVolumioMdnsRecord): Promise<IDiscoveryMatch> {
    const type = normalizeType(recordArg.type);
    const properties = { ...recordArg.txt, ...recordArg.properties };
    const uuid = valueForKey(properties, 'UUID') || valueForKey(properties, 'uuid') || valueForKey(properties, 'id');
    const name = cleanName(valueForKey(properties, 'volumioName') || valueForKey(properties, 'name') || recordArg.name || recordArg.hostname);
    const matched = type === volumioMdnsType || Boolean(uuid && includesVolumio(name || recordArg.name)) || includesVolumio(`${recordArg.name || ''} ${type}`);

    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'mDNS record is not a Volumio advertisement.' };
    }

    return {
      matched: true,
      confidence: uuid ? 'certain' : 'high',
      reason: 'mDNS record matches Volumio zeroconf metadata.',
      normalizedDeviceId: uuid,
      candidate: {
        source: 'mdns',
        integrationDomain: volumioDomain,
        id: uuid,
        host: recordArg.host || recordArg.addresses?.[0],
        port: recordArg.port || volumioDefaultPort,
        name,
        manufacturer: 'Volumio',
        model: valueForKey(properties, 'hardware') || valueForKey(properties, 'model'),
        metadata: {
          mdnsName: recordArg.name,
          mdnsType: recordArg.type,
          txt: properties,
        },
      },
    };
  }
}

export class VolumioManualMatcher implements IDiscoveryMatcher<IVolumioManualEntry> {
  public id = 'volumio-manual-match';
  public source = 'manual' as const;
  public description = 'Recognize manual Volumio setup entries.';

  public async matches(inputArg: IVolumioManualEntry): Promise<IDiscoveryMatch> {
    const matched = Boolean(
      inputArg.host
      || includesVolumio(inputArg.name)
      || includesVolumio(inputArg.model)
      || includesVolumio(inputArg.hardware)
      || includesVolumio(inputArg.manufacturer)
      || inputArg.metadata?.volumio
    );

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

    const id = inputArg.uuid || inputArg.id;
    return {
      matched: true,
      confidence: inputArg.host ? 'high' : 'medium',
      reason: 'Manual entry can start Volumio setup.',
      normalizedDeviceId: id,
      candidate: {
        source: 'manual',
        integrationDomain: volumioDomain,
        id,
        host: inputArg.host,
        port: inputArg.port || volumioDefaultPort,
        name: inputArg.name,
        manufacturer: inputArg.manufacturer || 'Volumio',
        model: inputArg.model || inputArg.hardware,
        metadata: {
          ...inputArg.metadata,
          hardware: inputArg.hardware,
        },
      },
    };
  }
}

export class VolumioCandidateValidator implements IDiscoveryValidator {
  public id = 'volumio-candidate-validator';
  public description = 'Validate Volumio candidate metadata.';

  public async validate(candidateArg: IDiscoveryCandidate): Promise<IDiscoveryMatch> {
    const matched = candidateArg.integrationDomain === volumioDomain
      || includesVolumio(candidateArg.manufacturer)
      || includesVolumio(candidateArg.model)
      || includesVolumio(candidateArg.name)
      || Boolean(candidateArg.metadata?.volumio || candidateArg.metadata?.volumioName);

    return {
      matched,
      confidence: matched && candidateArg.host ? 'high' : matched ? 'medium' : 'low',
      reason: matched ? 'Candidate has Volumio metadata.' : 'Candidate is not Volumio.',
      candidate: matched ? candidateArg : undefined,
      normalizedDeviceId: candidateArg.id,
      metadata: matched ? { validatedAs: volumioDomain } : undefined,
    };
  }
}

export const createVolumioDiscoveryDescriptor = (): DiscoveryDescriptor => {
  return new DiscoveryDescriptor({ integrationDomain: volumioDomain, displayName: 'Volumio' })
    .addMatcher(new VolumioMdnsMatcher())
    .addMatcher(new VolumioManualMatcher())
    .addValidator(new VolumioCandidateValidator());
};

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

const valueForKey = (recordArg: Record<string, string | undefined> | undefined, keyArg: string): string | undefined => {
  if (!recordArg) {
    return undefined;
  }
  const lowerKey = keyArg.toLowerCase();
  for (const [key, value] of Object.entries(recordArg)) {
    if (key.toLowerCase() === lowerKey) {
      return value;
    }
  }
  return undefined;
};

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

const includesVolumio = (valueArg: string | undefined): boolean => {
  return Boolean(valueArg?.toLowerCase().includes('volumio'));
};
