import { DiscoveryDescriptor } from '../../core/classes.discoverydescriptor.js';
import type { IDiscoveryCandidate, IDiscoveryMatch, IDiscoveryMatcher, IDiscoveryValidator } from '../../core/types.js';
import type { IYeelightManualEntry, IYeelightMdnsRecord, IYeelightSsdpRecord } from './yeelight.types.js';

const defaultPort = 55443;

export class YeelightSsdpMatcher implements IDiscoveryMatcher<IYeelightSsdpRecord> {
  public id = 'yeelight-ssdp-match';
  public source = 'ssdp' as const;
  public description = 'Recognize Yeelight local LAN SSDP wifi_bulb responses.';

  public async matches(recordArg: IYeelightSsdpRecord): Promise<IDiscoveryMatch> {
    const headers = this.normalizedHeaders(recordArg);
    const location = headers.location;
    const support = headers.support || '';
    if (!headers.id || !location || !(headers.st === 'wifi_bulb' || support.includes('set_power') || headers.model)) {
      return { matched: false, confidence: 'low', reason: 'SSDP response is not a Yeelight wifi_bulb advertisement.' };
    }
    const endpoint = this.parseLocation(location);
    return {
      matched: true,
      confidence: headers.id && endpoint.host ? 'certain' : 'high',
      reason: 'SSDP response contains Yeelight LAN control capabilities.',
      normalizedDeviceId: headers.id,
      candidate: {
        source: 'ssdp',
        integrationDomain: 'yeelight',
        id: headers.id,
        host: endpoint.host,
        port: endpoint.port || defaultPort,
        name: headers.name,
        manufacturer: 'Yeelight',
        model: headers.model,
        metadata: {
          location,
          support: support.split(' ').filter(Boolean),
          firmwareVersion: headers.fw_ver,
          capabilities: headers,
        },
      },
    };
  }

  private normalizedHeaders(recordArg: IYeelightSsdpRecord): Record<string, string | undefined> {
    const source = { ...recordArg.headers, ...recordArg.ssdp_headers, ...recordArg } as Record<string, unknown>;
    const normalized: Record<string, string | undefined> = {};
    for (const [key, value] of Object.entries(source)) {
      if (typeof value === 'string') {
        normalized[key.toLowerCase()] = value;
      }
    }
    return normalized;
  }

  private parseLocation(locationArg: string): { host?: string; port?: number } {
    try {
      const parsed = new URL(locationArg);
      return { host: parsed.hostname, port: parsed.port ? Number(parsed.port) : undefined };
    } catch {
      const match = locationArg.match(/([0-9a-z.-]+):(\d+)/i);
      return match ? { host: match[1], port: Number(match[2]) } : {};
    }
  }
}

export class YeelightMdnsMatcher implements IDiscoveryMatcher<IYeelightMdnsRecord> {
  public id = 'yeelight-mdns-match';
  public source = 'mdns' as const;
  public description = 'Recognize Yeelight zeroconf records from yeelink names and _miio._udp.local.';

  public async matches(recordArg: IYeelightMdnsRecord): Promise<IDiscoveryMatch> {
    const type = (recordArg.type || '').toLowerCase().replace(/\.$/, '');
    const name = (recordArg.name || recordArg.hostname || '').toLowerCase();
    const txt = recordArg.txt || recordArg.properties || {};
    const model = txt.model || txt.modelid || recordArg.txt?.md || undefined;
    const matched = name.startsWith('yeelink-') || type === '_miio._udp.local' || Boolean(model?.startsWith('YL'));
    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'mDNS record is not a Yeelight advertisement.' };
    }
    const host = recordArg.host || recordArg.hostname || recordArg.addresses?.[0];
    const deviceId = txt.id || txt.mac || txt.serial || recordArg.name || host;
    return {
      matched: true,
      confidence: host ? 'high' : 'medium',
      reason: 'mDNS record matches Yeelight zeroconf metadata.',
      normalizedDeviceId: deviceId,
      candidate: {
        source: 'mdns',
        integrationDomain: 'yeelight',
        id: deviceId,
        host,
        port: recordArg.port || defaultPort,
        name: recordArg.name,
        manufacturer: 'Yeelight',
        model,
        metadata: {
          mdnsName: recordArg.name,
          mdnsType: recordArg.type,
          txt,
        },
      },
    };
  }
}

export class YeelightManualMatcher implements IDiscoveryMatcher<IYeelightManualEntry> {
  public id = 'yeelight-manual-match';
  public source = 'manual' as const;
  public description = 'Recognize manual Yeelight setup entries by host or Yeelight metadata.';

  public async matches(inputArg: IYeelightManualEntry): Promise<IDiscoveryMatch> {
    const model = inputArg.model?.toLowerCase() || '';
    const name = inputArg.name?.toLowerCase() || '';
    const manufacturer = inputArg.manufacturer?.toLowerCase() || '';
    const matched = Boolean(inputArg.host || inputArg.snapshot || inputArg.capabilities || inputArg.metadata?.yeelight || model.includes('yeelight') || name.includes('yeelight') || manufacturer === 'yeelight');
    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'Manual entry does not contain Yeelight setup hints.' };
    }
    return {
      matched: true,
      confidence: inputArg.host ? 'high' : 'medium',
      reason: 'Manual entry can start Yeelight setup.',
      normalizedDeviceId: inputArg.id || inputArg.host,
      candidate: {
        source: 'manual',
        integrationDomain: 'yeelight',
        id: inputArg.id,
        host: inputArg.host,
        port: inputArg.port || defaultPort,
        name: inputArg.name,
        manufacturer: 'Yeelight',
        model: inputArg.model,
        metadata: {
          ...inputArg.metadata,
          properties: inputArg.properties,
          capabilities: inputArg.capabilities,
          snapshot: inputArg.snapshot,
        },
      },
    };
  }
}

export class YeelightCandidateValidator implements IDiscoveryValidator {
  public id = 'yeelight-candidate-validator';
  public description = 'Validate Yeelight candidates before starting local LAN setup.';

  public async validate(candidateArg: IDiscoveryCandidate): Promise<IDiscoveryMatch> {
    const manufacturer = candidateArg.manufacturer?.toLowerCase() || '';
    const model = candidateArg.model?.toLowerCase() || '';
    const name = candidateArg.name?.toLowerCase() || '';
    const metadata = candidateArg.metadata || {};
    const matched = candidateArg.integrationDomain === 'yeelight' || manufacturer === 'yeelight' || model.includes('yeelight') || name.includes('yeelight') || name.startsWith('yeelink-') || Boolean(metadata.capabilities || metadata.yeelight);
    return {
      matched,
      confidence: matched && candidateArg.host ? 'high' : matched ? 'medium' : 'low',
      reason: matched ? 'Candidate has Yeelight metadata.' : 'Candidate is not Yeelight.',
      candidate: matched ? { ...candidateArg, port: candidateArg.port || defaultPort } : undefined,
      normalizedDeviceId: candidateArg.id || candidateArg.host,
    };
  }
}

export const createYeelightDiscoveryDescriptor = (): DiscoveryDescriptor => {
  return new DiscoveryDescriptor({ integrationDomain: 'yeelight', displayName: 'Yeelight' })
    .addMatcher(new YeelightSsdpMatcher())
    .addMatcher(new YeelightMdnsMatcher())
    .addMatcher(new YeelightManualMatcher())
    .addValidator(new YeelightCandidateValidator());
};
