import { Injectable } from '@nestjs/common';
import { Anomaly } from '@prisma/client';
import { Interval } from 'date-fns';
import { AnomalyDetectorService } from './anomaly-detectors/anomaly-detector.service';
import { DriverCertificateDetector } from './anomaly-detectors/driver-certificate.detector';
import { DriverCodriverSameDetector } from './anomaly-detectors/driver-codriver-same.detector';
import { EmployeesLoggedInDetector } from './anomaly-detectors/employees-logged-in.detector';
// import { OneTourAtATimeDetector } from './anomaly-detectors/one-tour-at-a-time.detector';
import { AnomalyCreateDto } from '../feat-business-objects/entities/anomaly/anomalies.dto';
import { AnomalyService } from '../feat-business-objects/entities/anomaly/anomaly.service';
import { AnomalyDetector } from './anomaly-detectors/anomaly-detector.class';
import { WorkingEmployeeNotInDistributionSystemDetector } from './anomaly-detectors/working-employee-not-in-distribution-system.detector';

@Injectable()
export class AnomaliesService {
  private detectors: AnomalyDetector[];

  constructor(
    private readonly anomalyDetectorService: AnomalyDetectorService,
    private readonly anomalyService: AnomalyService,
  ) {
    this.detectors = [
      new EmployeesLoggedInDetector(this.anomalyDetectorService),
      new DriverCertificateDetector(this.anomalyDetectorService),
      new DriverCodriverSameDetector(this.anomalyDetectorService),
      // new OneTourAtATimeDetector(this.anomalyDetectorService),
      new WorkingEmployeeNotInDistributionSystemDetector(
        this.anomalyDetectorService,
      ),
    ];
  }

  detectAnomalies(interval: Interval): Promise<Anomaly[]> {
    return Promise.all(
      this.detectors.map((detector) => detector.detect(interval)),
    ).then(async (anomaliesPerDetector) => {
      const anomalies = anomaliesPerDetector.flat();
      const anomalyEntities: Anomaly[] = [];
      for (const anomaly of anomalies) {
        if (anomaly) {
          anomalyEntities.push(
            await this.anomalyService.createEvenIfExists(
              anomaly as AnomalyCreateDto,
            ),
          );
        }
      }
      return anomalyEntities;
    });
  }

  async validateAnomalies(): Promise<Anomaly[]> {
    const anomalies = await this.anomalyService.findAllUnsolved();

    return Promise.all(
      anomalies.map((anomaly) =>
        this.detectors
          .find((detector) => detector.type === anomaly.type)
          .validate(anomaly),
      ),
    ).then(async (anomalies) => {
      const validatedAnomalies = anomalies.flat();
      const updatedAnomalies: Anomaly[] = [];
      for (const anomaly of validatedAnomalies) {
        if (anomaly.resolvedAt) {
          updatedAnomalies.push(
            await this.anomalyService.resolveAnomaly(anomaly.id),
          );
        }
      }
      return updatedAnomalies;
    });
  }
}
