import { DiscoveryDescriptor } from '../../core/classes.discoverydescriptor.js';
import type { IDiscoveryCandidate, IDiscoveryMatch, IDiscoveryMatcher, IDiscoveryValidator } from '../../core/types.js';
import { androidtvDefaultPort } from './androidtv.constants.js';
import type { IAndroidtvAdbHostRecord, IAndroidtvManualEntry, IAndroidtvMdnsRecord, TAndroidtvDeviceClass } from './androidtv.types.js';

export class AndroidtvMdnsMatcher implements IDiscoveryMatcher<IAndroidtvMdnsRecord> {
  public id = 'androidtv-mdns-match';
  public source = 'mdns' as const;
  public description = 'Recognize Android TV mDNS setup hints.';

  public async matches(recordArg: IAndroidtvMdnsRecord): Promise<IDiscoveryMatch> {
    const type = recordArg.type?.toLowerCase() || '';
    const name = recordArg.name || recordArg.txt?.fn || '';
    const model = recordArg.txt?.md || '';
    const matched = type.includes('androidtvremote') || this.hasAndroidTvHint(name, model);
    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'mDNS record does not contain Android TV hints.' };
    }
    const id = recordArg.txt?.id;
    return {
      matched: true,
      confidence: recordArg.host ? 'medium' : 'low',
      reason: 'mDNS record contains Android TV metadata.',
      normalizedDeviceId: id,
      candidate: {
        source: 'mdns',
        integrationDomain: 'androidtv',
        id,
        host: recordArg.host,
        port: androidtvDefaultPort,
        name,
        manufacturer: recordArg.txt?.mf,
        model,
        metadata: {
          type: recordArg.type,
          remotePort: recordArg.port,
          txt: recordArg.txt,
          deviceClass: this.deviceClass(recordArg.txt?.mf, model),
        },
      },
    };
  }

  private hasAndroidTvHint(...valuesArg: string[]): boolean {
    return valuesArg.some((valueArg) => {
      const value = valueArg.toLowerCase();
      return value.includes('android tv') || value.includes('androidtv') || value.includes('fire tv') || value.includes('firetv');
    });
  }

  private deviceClass(manufacturerArg?: string, modelArg?: string): TAndroidtvDeviceClass {
    const value = `${manufacturerArg || ''} ${modelArg || ''}`.toLowerCase();
    return value.includes('amazon') || value.includes('fire tv') || value.includes('firetv') ? 'firetv' : 'androidtv';
  }
}

export class AndroidtvManualMatcher implements IDiscoveryMatcher<IAndroidtvManualEntry> {
  public id = 'androidtv-manual-match';
  public source = 'manual' as const;
  public description = 'Recognize manual Android TV ADB setup entries.';

  public async matches(inputArg: IAndroidtvManualEntry): Promise<IDiscoveryMatch> {
    const matched = Boolean(inputArg.host || inputArg.deviceClass || inputArg.metadata?.androidtv || inputArg.metadata?.adb || this.hasAndroidTvHint(inputArg.name, inputArg.deviceName, inputArg.model, inputArg.manufacturer));
    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'Manual entry does not contain Android TV setup hints.' };
    }
    return {
      matched: true,
      confidence: inputArg.host ? 'high' : 'medium',
      reason: 'Manual entry can start Android TV ADB setup.',
      normalizedDeviceId: inputArg.id,
      candidate: {
        source: 'manual',
        integrationDomain: 'androidtv',
        id: inputArg.id,
        host: inputArg.host,
        port: inputArg.port || androidtvDefaultPort,
        name: inputArg.deviceName || inputArg.name,
        manufacturer: inputArg.manufacturer,
        model: inputArg.model,
        metadata: {
          ...inputArg.metadata,
          deviceClass: inputArg.deviceClass || this.deviceClass(inputArg.manufacturer, inputArg.model),
        },
      },
    };
  }

  private hasAndroidTvHint(...valuesArg: Array<string | undefined>): boolean {
    return valuesArg.some((valueArg) => {
      const value = valueArg?.toLowerCase() || '';
      return value.includes('android tv') || value.includes('androidtv') || value.includes('fire tv') || value.includes('firetv');
    });
  }

  private deviceClass(manufacturerArg?: string, modelArg?: string): TAndroidtvDeviceClass {
    const value = `${manufacturerArg || ''} ${modelArg || ''}`.toLowerCase();
    return value.includes('amazon') || value.includes('fire tv') || value.includes('firetv') ? 'firetv' : 'androidtv';
  }
}

export class AndroidtvAdbHostMatcher implements IDiscoveryMatcher<IAndroidtvAdbHostRecord> {
  public id = 'androidtv-adb-host-match';
  public source = 'custom' as const;
  public description = 'Recognize ADB host discovery records for Android TV.';

  public async matches(recordArg: IAndroidtvAdbHostRecord): Promise<IDiscoveryMatch> {
    const protocol = String(recordArg.protocol || recordArg.metadata?.protocol || '').toLowerCase();
    const service = String(recordArg.service || recordArg.metadata?.service || '').toLowerCase();
    const matched = protocol === 'adb' || service === 'adb' || recordArg.port === androidtvDefaultPort || Boolean(recordArg.metadata?.adb) || Boolean(recordArg.metadata?.androidtv);
    if (!matched) {
      return { matched: false, confidence: 'low', reason: 'Record is not an Android TV ADB host.' };
    }
    return {
      matched: true,
      confidence: recordArg.host && recordArg.port === androidtvDefaultPort ? 'certain' : recordArg.host ? 'high' : 'medium',
      reason: 'Record contains ADB host metadata.',
      normalizedDeviceId: recordArg.id || recordArg.macAddress || recordArg.serialNumber,
      candidate: {
        source: 'custom',
        integrationDomain: 'androidtv',
        id: recordArg.id,
        host: recordArg.host,
        port: recordArg.port || androidtvDefaultPort,
        name: recordArg.name,
        manufacturer: recordArg.manufacturer,
        model: recordArg.model,
        serialNumber: recordArg.serialNumber,
        macAddress: recordArg.macAddress,
        metadata: {
          ...recordArg.metadata,
          protocol: protocol || 'adb',
          deviceClass: recordArg.deviceClass || this.deviceClass(recordArg.manufacturer, recordArg.model),
        },
      },
    };
  }

  private deviceClass(manufacturerArg?: string, modelArg?: string): TAndroidtvDeviceClass {
    const value = `${manufacturerArg || ''} ${modelArg || ''}`.toLowerCase();
    return value.includes('amazon') || value.includes('fire tv') || value.includes('firetv') ? 'firetv' : 'androidtv';
  }
}

export class AndroidtvCandidateValidator implements IDiscoveryValidator {
  public id = 'androidtv-candidate-validator';
  public description = 'Validate Android TV ADB candidate metadata.';

  public async validate(candidateArg: IDiscoveryCandidate): Promise<IDiscoveryMatch> {
    const hint = this.hasAndroidTvHint(candidateArg) || candidateArg.integrationDomain === 'androidtv' || candidateArg.port === androidtvDefaultPort || Boolean(candidateArg.metadata?.adb);
    const matched = Boolean(hint && candidateArg.host);
    return {
      matched,
      confidence: matched && (candidateArg.id || candidateArg.macAddress || candidateArg.serialNumber) ? 'certain' : matched ? 'high' : 'low',
      reason: matched ? 'Candidate has Android TV ADB metadata and a host.' : 'Candidate is missing Android TV ADB metadata or host.',
      candidate: matched ? candidateArg : undefined,
      normalizedDeviceId: candidateArg.id || candidateArg.macAddress || candidateArg.serialNumber || candidateArg.host,
      metadata: {
        port: candidateArg.port || androidtvDefaultPort,
      },
    };
  }

  private hasAndroidTvHint(candidateArg: IDiscoveryCandidate): boolean {
    const values = [candidateArg.name, candidateArg.manufacturer, candidateArg.model, String(candidateArg.metadata?.deviceClass || ''), String(candidateArg.metadata?.protocol || '')];
    return values.some((valueArg) => {
      const value = valueArg?.toLowerCase() || '';
      return value.includes('android tv') || value.includes('androidtv') || value.includes('fire tv') || value.includes('firetv') || value === 'adb';
    });
  }
}

export const createAndroidtvDiscoveryDescriptor = (): DiscoveryDescriptor => {
  return new DiscoveryDescriptor({ integrationDomain: 'androidtv', displayName: 'Android Debug Bridge' })
    .addMatcher(new AndroidtvMdnsMatcher())
    .addMatcher(new AndroidtvManualMatcher())
    .addMatcher(new AndroidtvAdbHostMatcher())
    .addValidator(new AndroidtvCandidateValidator());
};
