import { Injectable } from '@nestjs/common';
import {
  AttendanceRegistration,
  Employee,
  PlannedTime,
  Prisma,
  Tour,
  WorkTime,
} from '@prisma/client';
import { Interval, addDays, addHours, endOfDay, startOfDay } from 'date-fns';
import { AttendanceRegistrationEvents } from 'src/modules/feat-business-objects/entities/attendance-registration/attendance-registration-events.enum';
import { AttendanceRegistrationService } from 'src/modules/feat-business-objects/entities/attendance-registration/attendance-registration.service';
import { EmployeeService } from 'src/modules/feat-business-objects/entities/employee/employee.service';
import { PlannedTimeService } from 'src/modules/feat-business-objects/entities/planned-time/planned-time.service';
import { TourService } from 'src/modules/feat-business-objects/entities/tour/tour.service';
import { WorkTimeService } from 'src/modules/feat-business-objects/entities/work-time/work-time.service';

export type AnomalySkeleton = Pick<
  Prisma.AnomalyCreateInput,
  | 'createdAt'
  | 'workedOnBy'
  | 'sleepTimer'
  | 'sleepSince'
  | 'resolvedAt'
  | 'manuallyResolved'
>;

@Injectable()
export class AnomalyDetectorService {
  constructor(
    private readonly attendanceRegistrationService: AttendanceRegistrationService,
    private readonly employeeService: EmployeeService,
    private readonly plannedTimeService: PlannedTimeService,
    private readonly tourService: TourService,
    private readonly workTimeService: WorkTimeService,
  ) {}

  /**
   * Find a list of tours in a given time interval
   * @param interval An interval defining the time range of tours
   * @returns
   */
  public findToursInInterval(
    interval: Interval,
    ordinanceTypes: string[] = [],
  ): Promise<Tour[]> {
    return this.tourService.findInInterval(interval, ordinanceTypes);
  }

  public findToursInIntervalWithEmployee(
    interval: Interval,
    employeeId: string,
    ordinanceType?: string,
  ): Promise<Tour[]> {
    return this.tourService.findInIntervalWithEmployee(
      interval,
      employeeId,
      ordinanceType ? [ordinanceType] : [],
    );
  }

  public findTourByOperationId(operationId): Promise<Tour> {
    return this.tourService.findByOperationId(operationId);
  }

  public findWorkTimeByPersonnelNumberForDay(
    personnelNumber: string,
    day: Date,
  ): Promise<WorkTime | undefined> {
    let startOfGivenDay: Date;
    if (day.getHours() >= 0 && day.getHours() < 2) {
      startOfGivenDay = addDays(day, -1);
    } else {
      startOfGivenDay = startOfDay(day);
    }
    const endOfGivenDay = endOfDay(startOfGivenDay);
    return this.workTimeService.findByPersonnelNumberInInterval(
      personnelNumber,
      { start: startOfGivenDay, end: endOfGivenDay },
    );
  }

  public findAttendanceRegistrationByPersonnelNumberForShift(
    personnelNumber: string,
    day: Date,
    eventTypes: AttendanceRegistrationEvents[] = [
      AttendanceRegistrationEvents.ClockedIn,
      AttendanceRegistrationEvents.ClockedOut,
    ],
  ): Promise<AttendanceRegistration[]> {
    let startOfGivenDay: Date;
    // TODO: Is there a better solution? This seems a bit flacky
    if (day.getHours() >= 0 && day.getHours() < 2) {
      startOfGivenDay = addDays(day, -1);
    } else {
      startOfGivenDay = startOfDay(day);
    }
    const endOfShift = addHours(day, 12);

    return this.attendanceRegistrationService.findByEmployeeInInterval(
      +personnelNumber,
      { start: startOfGivenDay, end: endOfShift },
      eventTypes,
    );
  }

  public findPlannedTimeByPersonnelNumberForDay(
    personnelNumber: string,
    day: Date,
  ): Promise<PlannedTime[]> {
    let startOfGivenDay: Date;
    if (day.getHours() >= 0 && day.getHours() < 3) {
      startOfGivenDay = addDays(day, -1);
    } else {
      startOfGivenDay = startOfDay(day);
    }
    const endOfShift = addHours(day, 12);
    return this.plannedTimeService.findByPersonnelNumberInInterval(
      personnelNumber,
      { start: startOfGivenDay, end: endOfShift },
    );
  }

  public findAttendanceRegistrationById(
    attendanceRegistrationId: number,
  ): Promise<AttendanceRegistration> {
    return this.attendanceRegistrationService.findById(
      attendanceRegistrationId,
    );
  }

  public findAttendanceRegistrationInInterval(
    interval: Interval,
    eventTypes: AttendanceRegistrationEvents[] = [
      AttendanceRegistrationEvents.ClockedIn,
      AttendanceRegistrationEvents.ClockedOut,
    ],
  ): Promise<AttendanceRegistration[]> {
    return this.attendanceRegistrationService.findInInterval(
      interval,
      eventTypes,
    );
  }

  public findEmployeeById(employeeId: string): Promise<Employee> {
    return this.employeeService.findById(employeeId);
  }

  public findEmployeeByPersonnelNumber(
    personnelNumber: string,
  ): Promise<Employee> {
    return this.employeeService.findByPersonnelNumber(personnelNumber);
  }
}
