import type { IntegrationRegistry } from './classes.integrationregistry.js';
import type {
  IDiscoveryCandidate,
  IDiscoveryContext,
  IDiscoveryMatch,
  TDiscoverySource,
} from './types.js';

export class DiscoveryEngine {
  constructor(private readonly integrationRegistry: IntegrationRegistry) {}

  public async runActiveDiscovery(contextArg: IDiscoveryContext = {}): Promise<IDiscoveryCandidate[]> {
    const candidates: IDiscoveryCandidate[] = [];

    for (const integration of this.integrationRegistry.list()) {
      const descriptor = integration.discoveryDescriptor;
      for (const probe of descriptor.getProbes()) {
        const result = await probe.probe(contextArg);
        for (const candidate of result.candidates) {
          candidates.push({
            ...candidate,
            integrationDomain: candidate.integrationDomain ?? integration.domain,
          });
        }
      }
    }

    return candidates;
  }

  public async matchExistingData<TInput>(
    sourceArg: TDiscoverySource,
    inputArg: TInput,
    contextArg: IDiscoveryContext = {}
  ): Promise<IDiscoveryMatch[]> {
    const matches: IDiscoveryMatch[] = [];

    for (const integration of this.integrationRegistry.list()) {
      const descriptor = integration.discoveryDescriptor;
      for (const matcher of descriptor.getMatchers()) {
        if (matcher.source !== sourceArg) {
          continue;
        }
        const result = await matcher.matches(inputArg, contextArg);
        if (result.matched) {
          matches.push(result);
        }
      }
    }

    return matches;
  }

  public async validateCandidate(
    candidateArg: IDiscoveryCandidate,
    contextArg: IDiscoveryContext = {}
  ): Promise<IDiscoveryMatch[]> {
    const matches: IDiscoveryMatch[] = [];
    const integrations = candidateArg.integrationDomain
      ? this.integrationRegistry.list().filter((integrationArg) => integrationArg.domain === candidateArg.integrationDomain)
      : this.integrationRegistry.list();

    for (const integration of integrations) {
      for (const validator of integration.discoveryDescriptor.getValidators()) {
        const result = await validator.validate(candidateArg, contextArg);
        if (result.matched) {
          matches.push(result);
        }
      }
    }

    return matches;
  }
}
