import { Injectable } from '@nestjs/common';
import { from, map, of, switchMap } from 'rxjs';

import { TransformersService } from '../feat-transformers/transformers.service';
import { FinalRepository } from './final.repository';
import { AttendanceRegistrationLoader } from './loaders/attendance-registration.loader';
import { EmployeeDispoliveLoader } from './loaders/employee-dispolive';
import { EmployeeDyflexisLoader } from './loaders/employee-dyflexis';
import { PatientLoader } from './loaders/patient.loader';
import { PlannedTimeLoader } from './loaders/planned-time.loader';
import { TourLoader } from './loaders/tour.loader';
import { WorkTimeLoader } from './loaders/work-time.loader';

@Injectable()
export class LoadersService {
  constructor(
    private readonly repository: FinalRepository,
    private readonly transformersService: TransformersService,
  ) {}

  loadTours() {
    return from(this.transformersService.findTours()).pipe(
      switchMap((stagingTours) => {
        if (stagingTours.length === 0) {
          return of({
            staging: [],
            final: [],
          });
        }
        const { startDate: firstTourDate } = stagingTours.at(0);
        const { startDate: lastTourDate } = stagingTours.at(-1);
        return from(
          this.repository.findToursInInterval(
            {
              start: firstTourDate,
              end: lastTourDate,
            },
            stagingTours.map(({ operationId }) => operationId),
          ),
        ).pipe(
          map((finalTours) => ({
            final: finalTours,
            staging: stagingTours,
          })),
        );
      }),
      map(({ staging, final }) =>
        TourLoader.determineLoadOperations(staging, final),
      ),
      switchMap(({ deleted, updated, created }) => {
        return from(
          Promise.all([
            this.repository.deleteTours(deleted),
            this.repository.updateTours(updated),
            this.repository.createTours(created),
          ]),
        );
      }),
    );
  }

  loadPatients() {
    return from(this.transformersService.findPatients()).pipe(
      switchMap((stagingPatients) =>
        from(this.repository.findPatients()).pipe(
          map((finalPatients) => ({
            final: finalPatients,
            staging: stagingPatients,
          })),
        ),
      ),
      map(({ staging, final }) =>
        PatientLoader.determineLoadOperations(staging, final),
      ),
      switchMap(({ deleted, updated, created }) =>
        from(
          Promise.all([
            this.repository.deletePatients(deleted),
            this.repository.updatePatients(updated),
            this.repository.createPatients(created),
          ]),
        ),
      ),
    );
  }

  loadDyflexisEmployees() {
    return from(this.transformersService.findDyflexisEmployees()).pipe(
      switchMap((stagingEmployees) =>
        from(this.repository.findEmployeeDyflexis()).pipe(
          map((finalEmployees) => ({
            final: finalEmployees,
            staging: stagingEmployees,
          })),
        ),
      ),
      map(({ staging, final }) =>
        EmployeeDyflexisLoader.determineLoadOperations(staging, final),
      ),
      switchMap(({ deleted, updated, created }) =>
        from(
          Promise.all([
            this.repository.deleteEmployeeDyflexis(deleted),
            this.repository.updateEmployeeDyflexis(updated),
            this.repository.createEmployeeDyflexis(created),
          ]),
        ),
      ),
    );
  }

  loadDispoliveEmployees() {
    return from(this.transformersService.findDispoliveEmployees()).pipe(
      switchMap((stagingEmployees) =>
        from(this.repository.findEmployeeDispolive()).pipe(
          map((finalEmployees) => ({
            final: finalEmployees,
            staging: stagingEmployees,
          })),
        ),
      ),
      map(({ staging, final }) =>
        EmployeeDispoliveLoader.determineLoadOperations(staging, final),
      ),
      switchMap(({ deleted, updated, created }) =>
        from(
          Promise.all([
            this.repository.deleteEmployeeDispolive(deleted),
            this.repository.updateEmployeeDispolive(updated),
            this.repository.createEmployeeDispolive(created),
          ]),
        ),
      ),
    );
  }

  loadAttendanceRegistrations() {
    return from(this.transformersService.findAttendanceRegistrations()).pipe(
      switchMap((stagingAttendanceRegistrations) => {
        if (stagingAttendanceRegistrations.length === 0) {
          return of({
            staging: [],
            final: [],
          });
        }
        const { dateTime: firstAttendanceRegistration } = [
          ...stagingAttendanceRegistrations,
        ].shift();
        const { dateTime: lastAttendanceRegistration } = [
          ...stagingAttendanceRegistrations,
        ].pop();
        return from(
          this.repository.findAttendanceRegistrationsInInterval({
            start: firstAttendanceRegistration,
            end: lastAttendanceRegistration,
          }),
        ).pipe(
          map((finalAttendanceRegistrations) => ({
            final: finalAttendanceRegistrations,
            staging: stagingAttendanceRegistrations,
          })),
        );
      }),
      map(({ staging, final }) =>
        AttendanceRegistrationLoader.determineLoadOperations(staging, final),
      ),
      switchMap(({ deleted, updated, created }) => {
        return from(
          Promise.all([
            this.repository.deleteAttendanceRegistrations(deleted),
            this.repository.updateAttendanceRegistrations(updated),
            this.repository.createAttendanceRegistrations(created),
          ]),
        );
      }),
    );
  }

  loadPlannedTimes() {
    return from(this.transformersService.findPlannedTimes()).pipe(
      switchMap((stagingPlannedTimes) => {
        if (stagingPlannedTimes.length === 0) {
          return of({
            staging: [],
            final: [],
          });
        }
        const { startDate: firstPlannedTime } = [
          ...stagingPlannedTimes,
        ].shift();
        const { startDate: lastPlannedTime } = [...stagingPlannedTimes].pop();
        return from(
          this.repository.findPlannedTimesInInterval({
            start: firstPlannedTime,
            end: lastPlannedTime,
          }),
        ).pipe(
          map((finalPlannedTimes) => ({
            final: finalPlannedTimes,
            staging: stagingPlannedTimes,
          })),
        );
      }),
      map(({ staging, final }) =>
        PlannedTimeLoader.determineLoadOperations(staging, final),
      ),
      switchMap(({ deleted, updated, created }) => {
        return from(
          Promise.all([
            this.repository.deletePlannedTimes(deleted),
            this.repository.updatePlannedTimes(updated),
            this.repository.createPlannedTimes(created),
          ]),
        );
      }),
    );
  }

  loadWorkTimes() {
    return from(this.transformersService.findWorkTimes()).pipe(
      switchMap((stagingWorkTimes) => {
        if (stagingWorkTimes.length === 0) {
          return of({
            staging: [],
            final: [],
          });
        }
        const { startDate: firstWorkTime } = [...stagingWorkTimes].shift();
        const { startDate: lastWorkTime } = [...stagingWorkTimes].pop();
        return from(
          this.repository.findWorkTimesInInterval({
            start: firstWorkTime,
            end: lastWorkTime,
          }),
        ).pipe(
          map((finalTours) => ({
            final: finalTours,
            staging: stagingWorkTimes,
          })),
        );
      }),
      map(({ staging, final }) =>
        WorkTimeLoader.determineLoadOperations(staging, final),
      ),
      switchMap(({ deleted, updated, created }) => {
        return from(
          Promise.all([
            this.repository.deleteWorkTimes(deleted),
            this.repository.updateWorkTimes(updated),
            this.repository.createWorkTimes(created),
          ]),
        );
      }),
    );
  }
}
