import { Injectable, Logger } from '@nestjs/common';
import { Anomaly, SolutionTarget, SolutionTargetStep } from '@prisma/client';
import { AnomalyAsssociatedEntities, AnomalyCreateDto } from './anomalies.dto';
import { AnomalyTypes } from './anomaly-types.enum';
import { AnomalyRepository } from './anomaly.repository';
import { TourService } from '../tour/tour.service';
import { EmployeeService } from '../employee/employee.service';
import { PlannedTimeService } from '../planned-time/planned-time.service';
import { WorkTimeService } from '../work-time/work-time.service';
import { AttendanceRegistrationService } from '../attendance-registration/attendance-registration.service';

@Injectable()
export class AnomalyService {
  private logger = new Logger(AnomalyService.name);

  constructor(
    private readonly repository: AnomalyRepository,
    private readonly attendanceRegistrationService: AttendanceRegistrationService,
    private readonly employeeService: EmployeeService,
    private readonly plannedTimeService: PlannedTimeService,
    private readonly tourService: TourService,
    private readonly workTimeService: WorkTimeService,
  ) {}

  async createEvenIfExists(anomaly: AnomalyCreateDto): Promise<Anomaly> {
    return this.repository.createEvenIfExists(anomaly);
  }

  async resolveAnomaly(anomalyId: string): Promise<Anomaly> {
    return this.repository.resolveAnomaly(anomalyId);
  }

  async snoozeAnomaly(anomalyId: string): Promise<Anomaly> {
    return this.repository.snoozeAnomaly(anomalyId);
  }

  async acceptAnomaly(anomalyId: string): Promise<Anomaly> {
    return this.repository.acceptAnomaly(anomalyId);
  }

  async findAllUnsolved(): Promise<Anomaly[]> {
    return this.repository.findAllUnsolved();
  }

  async findAllVisible(
    skip?: number,
    take?: number,
    types: AnomalyTypes[] = [],
  ): Promise<Anomaly[]> {
    return this.repository.findAllVisible(skip, take, types);
  }

  async findById(anomalyId: string): Promise<Anomaly> {
    return this.repository.findById(anomalyId);
  }

  async countVisibleAnomalies(types: AnomalyTypes[] = []): Promise<number> {
    return this.repository.countVisibleAnomalies(types);
  }

  async getAssociatedEntities(
    anomalyId: string,
  ): Promise<AnomalyAsssociatedEntities> {
    const { associatedEntities } = await this.findById(anomalyId);

    const entityList = await Promise.all(
      Object.entries(associatedEntities).map(
        async ([type, ids]: [string, { id: string }[]]) => {
          switch (type) {
            case 'tour':
              return Promise.all(
                ids.map(({ id }) => this.tourService.findById(id)),
              ).then((tour) => ({ tour }));
            case 'plannedTime':
              return Promise.all(
                ids.map(({ id }) => this.plannedTimeService.findById(id)),
              ).then((plannedTime) => ({ plannedTime }));
            case 'workTime':
              return Promise.all(
                ids.map(({ id }) => this.workTimeService.findById(id)),
              ).then((workTime) => ({ workTime }));
            case 'attendanceRegistration':
              return Promise.all(
                ids.map(({ id }) =>
                  this.attendanceRegistrationService.findById(+id),
                ),
              ).then((attendanceRegistration) => ({ attendanceRegistration }));
            case 'employee':
              return Promise.all(
                ids.map(({ id }) => this.employeeService.findById(id)),
              ).then((employee) => ({ employee }));
            default:
              this.logger.error(
                `Associated Entity type not implemented: ${type}`,
              );
          }
        },
      ),
    );
    return entityList.reduce(
      (sum, cur) => ({
        ...sum,
        ...cur,
      }),
      {},
    );
  }

  async findSolutionTargetsByAnomalyId(
    anomalyId: string,
  ): Promise<SolutionTarget[]> {
    return this.repository.findSolutionTargetsByAnomalyId(anomalyId);
  }

  async findSolutionTargetStepsBySolutionTargetId(
    solutionTargetId: string,
  ): Promise<SolutionTargetStep[]> {
    return this.repository.findSolutionTargetStepsBySolutionTargetId(
      solutionTargetId,
    );
  }
}
