import mongoose from 'mongoose';

import { BBCDateModel, IBBCDateDoc } from '../models/BBCDate.model';
import { BBCSheetModel } from '../models/BBCSheet.model';
import { getAvailabilitySigns } from './availability.db';
import { ECollaterals } from '../enums/collaterals.enum';
import { MODEL_NAMES } from '../models/_models';

export const getBBCDateDoc = async (bbcDateId: string) => {
  return BBCDateModel.findById(bbcDateId).lean();
};

export const getBBCDatesByBorrower = async (borrowerId: string, sortOrder: 1 | -1 = 1) => {
  return BBCDateModel.aggregate<IBBCDateDoc>([
    {
      $match: {
        borrowerId: new mongoose.Types.ObjectId(borrowerId),
      },
    },
    {
      $sort: { bbcDate: sortOrder },
    },
  ]);
};

export const getLatestBBCDates = async (borrowerId: string, date?: Date, repo = BBCDateModel) => {
  const match: {
    borrowerId: mongoose.Types.ObjectId;
    bbcDate?: { $lte: Date };
  } = {
    borrowerId: new mongoose.Types.ObjectId(borrowerId),
  };
  if (date) {
    match.bbcDate = { $lte: date };
  }

  return repo.aggregate<IBBCDateDoc>([{ $match: match }, { $sort: { bbcDate: -1 } }]);
};

export const getLatestSignedBBCDateDoc = async (borrowerId: string, date?: Date,): Promise<IBBCDateDoc | null> => {
  const match: {
    borrowerId: mongoose.Types.ObjectId;
    bbcDate?: { $lte: Date };
  } = {
    borrowerId: new mongoose.Types.ObjectId(borrowerId),
  };
  if (date) {
    match.bbcDate = { $lte: date };
  }

  const bbcDates = await getLatestBBCDates(borrowerId, date);
  if (!bbcDates.length) {
    return null;
  }

  let foundSignedBBC: IBBCDateDoc | null = null;
  let i = 0;
  while (!foundSignedBBC && i < bbcDates.length) {
    const bbcDateId = bbcDates[i]._id.toString();
    const signs = await getAvailabilitySigns([bbcDateId]);
    const availabilitySigns = signs.pop();
    if (availabilitySigns) {
      const totalSigns = availabilitySigns.signs.filter((sign) => !sign.revokedAt);
      foundSignedBBC = totalSigns.length === availabilitySigns.requiredSigns ? bbcDates[i] : null;
    }
    i++;
  }
  return foundSignedBBC;
};

export const getOlderBBCDates = async (borrowerId: string, bbcDate: Date) => {
  return BBCDateModel.aggregate<IBBCDateDoc>([
    {
      $match: {
        borrowerId: new mongoose.Types.ObjectId(borrowerId),
        bbcDate: { $lt: bbcDate },
      },
    },
    {
      $sort: { bbcDate: 1 },
    },
  ]);
};

export const getBBCDates = async (borrowerId: string, bbcDate: Date) => {
  return BBCDateModel.aggregate<IBBCDateDoc>([
    {
      $match: {
        borrowerId: new mongoose.Types.ObjectId(borrowerId),
        bbcDate: new Date(bbcDate),
      },
    },
    {
      $sort: { updatedAt: 1 },
    },
  ]);
};

export const getBBCDatesForPeriod = async (borrowerId: string, bbcDateStart: Date, bbcDateEnd: Date) => {
  return BBCDateModel.aggregate<IBBCDateDoc>([
    {
      $match: {
        $and: [
          { 'borrowerId': new mongoose.Types.ObjectId(borrowerId) },
          { 'bbcDate': { $gte: bbcDateStart } },
          { 'bbcDate': { $lte: bbcDateEnd } },
        ],
      },
    },
    {
      $sort: {
        'bbcDate': 1,
      },
    },
  ]);
};

export const getBBCDatesByType = (borrowerId: string, collateralType: ECollaterals[]) => {
  return BBCSheetModel.aggregate<IBBCDateDoc>([
    {
      $match: {
        'dataType': { $in: collateralType },
      },
    },
    {
      $lookup: {
        from: MODEL_NAMES.BBCDates,
        localField: 'bbcDateId',
        foreignField: '_id',
        as: 'bbc',
      },
    },
    {
      $unwind: {
        path: '$bbc',
      },
    },
    {
      $match: {
        'bbc.borrowerId': new mongoose.Types.ObjectId(borrowerId),
      },
    },
    {
      $group: {
        '_id': '$bbc._id',
        'borrowerId': {
          $first: '$bbc.borrowerId',
        },
        'bbcDate': {
          $first: '$bbc.bbcDate',
        },
        'createdAt': {
          $first: '$bbc.createdAt',
        },
        'updatedAt': {
          $first: '$bbc.updatedAt',
        },
        '__v': {
          $first: '$bbc.__v',
        },
      },
    },
    {
      $sort: {
        'bbcDate': -1,
      },
    },
  ]);
};

export const createBBCDate = async (borrowerId: string, bbcDate: Date) => {
  const newBBC = await BBCDateModel.create({ borrowerId: new mongoose.Types.ObjectId(borrowerId), bbcDate });
  return newBBC.save();
};
