import mongoose from 'mongoose';
import path from 'path';
import AWS from 'aws-sdk';

import {
  ITransactionAttachedFile,
  ITransactionAttachedFileDoc,
  TransactionAttachedFileModel,
} from '../models/TransactionAttachedFile.Model';

interface IAttachedFilesServiceDependencies {
  saveFilesToAws: boolean;
  rootDir: string;
  s3Read: AWS.S3;
  bucketName: string;
}

export class AttachedFilesService {
  private readonly saveFilesToAws: boolean;
  private readonly rootDir: string;
  private readonly s3Read: AWS.S3;
  private readonly bucketName: string;

  constructor(
    deps: IAttachedFilesServiceDependencies,
  ) {
    this.saveFilesToAws = deps.saveFilesToAws;
    this.rootDir = deps.rootDir;
    this.s3Read = deps.s3Read;
    this.bucketName = deps.bucketName;
  }


  async downloadAttachedFile(fileId: string) {
    const DBFile: ITransactionAttachedFileDoc = await TransactionAttachedFileModel.findById(fileId).lean();
    if (!DBFile) {
      return null;
    }
    return DBFile;
  }

  async getEffectiveDateList(borrowerId: string) {
    const result = await TransactionAttachedFileModel.aggregate<{ _id: null, effectiveDates: string[] }>([
      {
        $match: {
          borrowerId: new mongoose.Types.ObjectId(borrowerId),
        },
      },
      {
        $group: {
          _id: null,
          effectiveDates: { $addToSet: '$effectiveDate' },
        },
      },
      {
        $unwind: '$effectiveDates',
      },
      {
        $sort: { 'effectiveDates': -1 },
      },
      {
        $group: {
          _id: null,
          effectiveDates: { $push: '$effectiveDates' },
        },
      },
    ]);
    return result.reduce((acc, item) => [...acc, ...item.effectiveDates], <string[]>[]);
  }

  async getLatestEffectiveDate(borrowerId: string) {
    const latsEffectiveDateDoc = await TransactionAttachedFileModel.find({ borrowerId }).sort({ effectiveDate: -1 }).limit(1).lean();
    if (!latsEffectiveDateDoc.length) {
      return null;
    }
    return latsEffectiveDateDoc[0].effectiveDate;
  }

  async getAttachedFilesByEffectiveDate(effectiveDate: string) {
    return TransactionAttachedFileModel.find({ effectiveDate }).sort({ originalName: 1 });
  }

  async getAttachedFilesByEffectiveDatAndBorrowerId(effectiveDate: string, borrowerId: string) {
    return TransactionAttachedFileModel.find({ effectiveDate, borrowerId }).sort({ originalName: 1 });
  }

  async createAttachedFile(file: {
    filename: string,
    originalName: string
  }, userId: string, effectiveDate: string, borrowerId: string) {
    const newFile: ITransactionAttachedFile = {
      userId,
      filename: file.filename,
      originalName: file.originalName,
      effectiveDate,
      borrowerId: new mongoose.Types.ObjectId(borrowerId),
    };
    return await TransactionAttachedFileModel.create(newFile);
  }

  async getAttachedRealFilesByTransactionFileId(borrowerId: string, effectiveDate: string) {
    const DBFiles = await TransactionAttachedFileModel.find({ effectiveDate, borrowerId }).lean();
    if (this.saveFilesToAws) {
      const downloadedFiles = [];
      await Promise.all(DBFiles.map(async (file) => {
        const fileData = await this.s3Read.getObject({
          Bucket: this.bucketName,
          Key: file.filename,
        }).promise();
        downloadedFiles.push({
          filename: file.originalName,
          content: fileData.Body,
        });
      }));
      return downloadedFiles;
    } else {
      return DBFiles.map((file) => ({
        filename: file.originalName,
        path: path.resolve(this.rootDir, file.filename),
      }));
    }
  }
}
