import { Injectable } from '@nestjs/common';
import {
  AttendanceRegistration,
  DyflexisEmployee,
  Employee,
  Patient,
  PlannedTime,
  Prisma,
  Tour,
  WorkTime,
} from '@prisma/client';
import { Interval } from 'date-fns';

import { PrismaService } from 'src/core/database/prisma.service';
import { chunk } from 'src/utils';

@Injectable()
export class FinalRepository {
  constructor(private readonly prisma: PrismaService) {}

  findToursInInterval(
    interval: Interval,
    operationIds: string[],
  ): Promise<Tour[]> {
    return this.prisma.tour.findMany({
      where: {
        OR: [
          {
            AND: [
              {
                startDate: {
                  gte: new Date(interval.start),
                },
              },
              {
                startDate: {
                  lte: new Date(interval.end),
                },
              },
            ],
          },
          {
            operationId: {
              in: operationIds,
            },
          },
        ],
      },
    });
  }

  createTours(
    tourCreateInput: Prisma.TourCreateInput[],
  ): Promise<Prisma.BatchPayload[]> {
    const chunks = chunk(tourCreateInput, 25000);
    return Promise.all(
      chunks.map((chunk) =>
        this.prisma.tour.createMany({
          data: chunk,
          skipDuplicates: true,
        }),
      ),
    );
  }

  async updateTours(
    tourCreateInput: Prisma.TourCreateInput[],
  ): Promise<Prisma.BatchPayload[]> {
    await this.prisma.tour.deleteMany({
      where: {
        operationId: {
          in: tourCreateInput.map(({ operationId }) => operationId),
        },
      },
    });
    return this.createTours(tourCreateInput);
  }

  deleteTours(operationIds: string[]): Promise<Prisma.BatchPayload[]> {
    const chunks = chunk(operationIds, 20000);
    return Promise.all(
      chunks.map((chunk) =>
        this.prisma.tour.updateMany({
          where: {
            operationId: {
              in: chunk,
            },
          },
          data: {
            deletedAt: new Date(),
          },
        }),
      ),
    );
  }

  findPatients(): Promise<Patient[]> {
    return this.prisma.patient.findMany({
      orderBy: {
        surname: 'asc',
      },
    });
  }

  createPatients(
    patientCreateInput: Prisma.PatientCreateInput[],
  ): Promise<Prisma.BatchPayload> {
    return this.prisma.patient.createMany({
      data: patientCreateInput,
      skipDuplicates: true,
    });
  }

  async updatePatients(
    patientCreateInput: Prisma.PatientCreateInput[],
  ): Promise<Prisma.BatchPayload> {
    await this.deletePatients(patientCreateInput.map(({ id }) => id));
    return this.createPatients(patientCreateInput);
  }

  deletePatients(ids: string[]): Promise<Prisma.BatchPayload> {
    return this.prisma.patient.deleteMany({
      where: {
        id: {
          in: ids,
        },
      },
    });
  }

  findEmployeeDyflexis(): Promise<DyflexisEmployee[]> {
    return this.prisma.dyflexisEmployee.findMany({
      orderBy: {
        surname: 'asc',
      },
    });
  }

  createEmployeeDyflexis(
    employeeDyflexisCreateInput: Prisma.DyflexisEmployeeCreateInput[],
  ): Promise<Prisma.BatchPayload> {
    return this.prisma.dyflexisEmployee.createMany({
      data: employeeDyflexisCreateInput,
      skipDuplicates: true,
    });
  }

  async updateEmployeeDyflexis(
    employeeDyflexisCreateInput: Prisma.DyflexisEmployeeCreateInput[],
  ): Promise<Prisma.BatchPayload> {
    await this.deleteEmployeeDyflexis(
      employeeDyflexisCreateInput.map(({ id }) => id),
    );
    return this.createEmployeeDyflexis(employeeDyflexisCreateInput);
  }

  deleteEmployeeDyflexis(ids: number[]): Promise<Prisma.BatchPayload> {
    return this.prisma.dyflexisEmployee.deleteMany({
      where: {
        id: {
          in: ids,
        },
      },
    });
  }

  findEmployeeDispolive(): Promise<Employee[]> {
    return this.prisma.employee.findMany({
      orderBy: {
        surname: 'asc',
      },
    });
  }

  createEmployeeDispolive(
    employeeCreateInput: Prisma.EmployeeCreateInput[],
  ): Promise<Prisma.BatchPayload> {
    return this.prisma.employee.createMany({
      data: employeeCreateInput,
      skipDuplicates: true,
    });
  }

  async updateEmployeeDispolive(
    employeeCreateInput: Prisma.EmployeeCreateInput[],
  ): Promise<Prisma.BatchPayload> {
    await this.deleteEmployeeDispolive(employeeCreateInput.map(({ id }) => id));
    return this.createEmployeeDispolive(employeeCreateInput);
  }

  deleteEmployeeDispolive(ids: string[]): Promise<Prisma.BatchPayload> {
    return this.prisma.employee.deleteMany({
      where: {
        id: {
          in: ids,
        },
      },
    });
  }

  findAttendanceRegistrationsInInterval(
    interval: Interval,
  ): Promise<AttendanceRegistration[]> {
    return this.prisma.attendanceRegistration.findMany({
      where: {
        AND: [
          {
            dateTime: {
              gte: new Date(interval.start),
            },
          },
          {
            dateTime: {
              lte: new Date(interval.end),
            },
          },
        ],
      },
    });
  }

  createAttendanceRegistrations(
    attendanceRegistrationCreateInput: Prisma.AttendanceRegistrationCreateInput[],
  ): Promise<Prisma.BatchPayload> {
    return this.prisma.attendanceRegistration.createMany({
      data: attendanceRegistrationCreateInput,
      skipDuplicates: true,
    });
  }

  async updateAttendanceRegistrations(
    attendanceRegistrationCreateInput: Prisma.AttendanceRegistrationCreateInput[],
  ): Promise<Prisma.BatchPayload> {
    await this.prisma.attendanceRegistration.deleteMany({
      where: {
        attendanceRegistrationId: {
          in: attendanceRegistrationCreateInput.map(
            ({ attendanceRegistrationId }) => attendanceRegistrationId,
          ),
        },
      },
    });
    return this.createAttendanceRegistrations(
      attendanceRegistrationCreateInput,
    );
  }

  deleteAttendanceRegistrations(
    attendanceRegistrationIds: number[],
  ): Promise<Prisma.BatchPayload> {
    return this.prisma.attendanceRegistration.deleteMany({
      where: {
        attendanceRegistrationId: {
          in: attendanceRegistrationIds,
        },
      },
    });
  }

  findPlannedTimesInInterval(interval: Interval): Promise<PlannedTime[]> {
    return this.prisma.plannedTime.findMany({
      where: {
        AND: [
          {
            startDate: {
              gte: new Date(interval.start),
            },
          },
          {
            startDate: {
              lte: new Date(interval.end),
            },
          },
        ],
      },
    });
  }

  createPlannedTimes(
    plannedTimeCreateInput: Prisma.PlannedTimeCreateInput[],
  ): Promise<Prisma.BatchPayload> {
    return this.prisma.plannedTime.createMany({
      data: plannedTimeCreateInput,
      skipDuplicates: true,
    });
  }

  async updatePlannedTimes(
    plannedTimeCreateInput: Prisma.PlannedTimeCreateInput[],
  ): Promise<Prisma.BatchPayload> {
    await this.prisma.plannedTime.deleteMany({
      where: {
        id: {
          in: plannedTimeCreateInput.map(({ id }) => id),
        },
      },
    });
    return this.createPlannedTimes(plannedTimeCreateInput);
  }

  deletePlannedTimes(plannedTimeIds: string[]): Promise<Prisma.BatchPayload> {
    return this.prisma.plannedTime.deleteMany({
      where: {
        id: {
          in: plannedTimeIds,
        },
      },
    });
  }

  findWorkTimesInInterval(interval: Interval): Promise<WorkTime[]> {
    return this.prisma.workTime.findMany({
      where: {
        AND: [
          {
            startDate: {
              gte: new Date(interval.start),
            },
          },
          {
            startDate: {
              lte: new Date(interval.end),
            },
          },
        ],
      },
    });
  }

  createWorkTimes(
    workTimeCreateInput: Prisma.WorkTimeCreateInput[],
  ): Promise<Prisma.BatchPayload> {
    return this.prisma.workTime.createMany({
      data: workTimeCreateInput,
      skipDuplicates: true,
    });
  }

  async updateWorkTimes(
    workTimeCreateInput: Prisma.WorkTimeCreateInput[],
  ): Promise<Prisma.BatchPayload> {
    await this.prisma.workTime.deleteMany({
      where: {
        id: {
          in: workTimeCreateInput.map(({ id }) => id),
        },
      },
    });
    return this.createWorkTimes(workTimeCreateInput);
  }

  deleteWorkTimes(workTimeIds: string[]): Promise<Prisma.BatchPayload> {
    return this.prisma.workTime.deleteMany({
      where: {
        id: {
          in: workTimeIds,
        },
      },
    });
  }
}
