import mongoose from 'mongoose';

import { BorrowerCompliance } from '../models/BorrowerCompliance.model';
import { IBorrowerDoc } from '../models/Borrower.model';
import { IComplianceFile } from '../models/ComplianceItem.model';

import { ComplianceBorrowersService } from './compliance-borrowers.service';

export type BorrowerWithFiles = {
  _id: mongoose.Types.ObjectId;
  borrower: IBorrowerDoc;
  files: IComplianceFile[]
}

export class FileManagerService {

  constructor(
    private readonly getComplianceBorrowersService: () => ComplianceBorrowersService,
  ) {
  }

  async getAwsFileId(borrowerId: string, instanceId: string, fileId: string): Promise<any> {
    try {
      const borrower = await BorrowerCompliance
        .findOne({ '_id': borrowerId, 'items.instances._id': instanceId, 'items.instances.files._id': fileId })
        .select('+items.instances.files.fileId');
      if (borrower) {
        const files = borrower.items.reduce((acc, item) => {
          return [...acc, ...item.instances.reduce((accI, instance) => {
            return [...accI, ...instance.files];
          }, [])];
        }, []);
        const foundFile = files.find((file) => file._id.toString() === fileId);
        return foundFile.fileId;
      }
      return null;
    } catch (e) {
      console.error(e);
      return null;
    }
  }

  async uploadFile(complianceBorrowerId: string, instanceId: string, originalName: string, uuid: string, user): Promise<void> {
    try {
      const items = await this.findItemByInstanceId(instanceId);
      if (items.length > 0) {
        const itemId = items.pop().items._id;
        await BorrowerCompliance
          .findOneAndUpdate(
            { _id: complianceBorrowerId },
            {
              $set: {
                'items.$[item].instances.$[instance].submittedDate': (new Date()).toISOString(),
                'items.$[item].instances.$[instance].status': 'IN_REVIEW',
              },
              $push:
                {
                  'items.$[item].instances.$[instance].files':
                    {
                      'fileId': uuid,
                      'name': originalName,
                      'isNew': true,
                      'uploadedAt': new Date(),
                      'uploadedBy': user?._id,
                    },
                },
            },
            {
              arrayFilters: [
                { 'item._id': itemId },
                { 'instance._id': new mongoose.Types.ObjectId(instanceId) },
              ],
            },
          );
      }

    } catch (e) {
      console.error(e);
    }
  }

  async findItemByInstanceId(instanceId) {
    return BorrowerCompliance.aggregate([
      { $unwind: '$items' },
      { $unwind: '$items.instances' },
      { $match: { 'items.instances._id': new mongoose.Types.ObjectId(instanceId) } },
    ]);
  }

  async getFilesNewerThanDate(date: Date): Promise<BorrowerWithFiles[]> {
    const borrowerWithFiles: BorrowerWithFiles[] = await BorrowerCompliance.aggregate([
      { $unwind: '$items' },
      { $unwind: '$items.instances' },
      { $unwind: '$items.instances.files' },
      { $match: { 'items.instances.files.uploadedAt': { $gte: new Date(date) } } },
      {
        $group:
          {
            _id: '$_id',
            borrower: { $first: '$borrower' },
            files: { $addToSet: '$items.instances.files' },
          },
      },
    ]);
    return (await BorrowerCompliance.populate(borrowerWithFiles, { path: 'borrower' }) as unknown[]) as BorrowerWithFiles[];
  }

  async deleteFile(complianceBorrowerId: string, fileId: string): Promise<void> {
    try {
      await BorrowerCompliance
        .findOneAndUpdate(
          { _id: complianceBorrowerId },
          { '$pull': { 'items.$[].instances.$[].files': { _id: fileId } } },
        );
    } catch (e) {
      console.error(e);
    }
  }

  async getFilesByBorrowerId(borrowerId: string) {
    const foundComplianceBorrower = await BorrowerCompliance
      .findOne({ borrower: borrowerId })
      .select('+items.instances.files.fileId');
    return foundComplianceBorrower ? foundComplianceBorrower.items : [];
  }

  async updateAWSID(itemId, instanceId, fileId, awsFileId) {
    const complianceBorrowersService = this.getComplianceBorrowersService();
    const complianceBorrower = await complianceBorrowersService.getBorrowerByInstanceId(instanceId);
    await BorrowerCompliance
      .findOneAndUpdate(
        { _id: complianceBorrower._id },
        {
          $set: { 'items.$[item].instances.$[instance].files.$[file].fileId': awsFileId },
        },
        {
          arrayFilters: [
            { 'item.item': itemId },
            { 'instance._id': new mongoose.Types.ObjectId(instanceId) },
            { 'file._id': new mongoose.Types.ObjectId(fileId) },
          ],
        },
      );
  }

}
