import mongoose from 'mongoose';
import _ from 'lodash';

import {
  FinancialSpreading,
  FinancialSpreadingDTO,
  financialSpreadingParamsValidationSchema,
  IFinancialSpreading,
  IFinancialSpreadingParams, IFinancialSpreadingView,
} from '../models/FinancialSpreading.model';
import { getShiftedMonth } from '../helpers/date.helper';
import {
  EFinanceSpreadingBSTotal,
  EFinanceSpreadingPLTotal,
  EFinancialSpreadingType,
  financeSpreadingLists,
  FinancialSpreadingSheet,
  FinancialSpreadingSheetDTO,
  financialSpreadingTotalDictionary,
  IFinancialSpreadingSheet,
} from '../models/FinancialSpreadingSheet.model';
import { rawDataToDTO } from '../helpers/db-data.helper';

const getAllBorrowerSheets = async(params: IFinancialSpreadingParams) => {
  const allSheets = await FinancialSpreadingSheet.find({
    borrowerId: params.borrowerId,
    financialSpreadingType: params.financialSpreadingType,
  }).lean();
  return _.sortBy(allSheets, ['order', 'isTotal', 'suborder']);
}

export const getCreateAllBorrowerSheet = async (params: IFinancialSpreadingParams) => {
  const allSheets = await getAllBorrowerSheets(params);
  if (allSheets.length) {
    return allSheets;
  }
  const totalsMap = {
    [EFinancialSpreadingType.PROFIT_LOSS]: EFinanceSpreadingPLTotal,
    [EFinancialSpreadingType.BALANCE_SHEET]: EFinanceSpreadingBSTotal,
  };
  await Promise.all(Object.entries(financeSpreadingLists[params.financialSpreadingType]).map(async ([totalKey, isCalculated], order) => {
    const newPartialRow: Omit<IFinancialSpreadingSheet, 'isTotal'> = {
      borrowerId: new mongoose.Types.ObjectId(params.borrowerId),
      financialSpreadingType: params.financialSpreadingType,
      name: financialSpreadingTotalDictionary[totalKey],
      rowType: totalsMap[params.financialSpreadingType][totalKey],
      isCalculated,
      order,
      suborder: 0,
    };
    if (!isCalculated) {
      const newRow: IFinancialSpreadingSheet = {
        ...newPartialRow,
        name: financialSpreadingTotalDictionary[totalKey].toLowerCase(),
        isTotal: false,
      };
      const newRowItem = new FinancialSpreadingSheet(newRow);
      await newRowItem.save();
    }
    const newTotalRow: IFinancialSpreadingSheet = {
      ...newPartialRow,
      isTotal: true,
    };
    const newTotalRowItem = new FinancialSpreadingSheet(newTotalRow);
    await newTotalRowItem.save();
  }));
  return getAllBorrowerSheets(params);
}

const getClearData = (rawData: IFinancialSpreading[], rawSheets: IFinancialSpreadingSheet[]) => {
  const data = rawDataToDTO<FinancialSpreadingDTO, IFinancialSpreading>(FinancialSpreadingDTO, rawData);
  const sheets = rawDataToDTO<FinancialSpreadingSheetDTO, IFinancialSpreadingSheet>(FinancialSpreadingSheetDTO, rawSheets);
  return { data, sheets };
}

export const getFinancialSpreadingData = async (params: IFinancialSpreadingParams, monthDeep = 3) => {
  const validationRes = financialSpreadingParamsValidationSchema.validate(params);
  if (validationRes.error) {
    console.error(validationRes.error);
    return { data: [], sheets: [] };
  }
  const range = Array.from({ length: monthDeep }, (_, i) => i + 1);
  const shifts = range.reduce((acc, curr) => {
    acc[`minus_${curr}`] = -curr;
    return acc;
  }, { 'amount': 0 });
  const rawSheets = await getCreateAllBorrowerSheet(params);
  const rawData = await Promise.all(rawSheets.map(async (sheet) => {
    const combinedData: IFinancialSpreadingView = {
      _id: '',
      borrowerId: params.borrowerId,
      sheetId: sheet._id.toString(),
      year: params.selectedMonth.year,
      month: params.selectedMonth.month,
      amount: 0,
      minus_1: 0,
      minus_2: 0,
      minus_3: 0,
    };
    await Promise.all(Object.entries(shifts).map(async ([shiftName, shiftValue]) => {
      const filter = {
        borrowerId: params.borrowerId,
        sheetId: sheet._id,
        ...getShiftedMonth(params.selectedMonth, shiftValue),
      };
      const foundData = await FinancialSpreading.findOne(filter).lean();
      if (shiftValue === 0) {
        combinedData._id = foundData ? foundData._id.toString() : `new_${sheet._id}`;
      }
      combinedData[shiftName] = foundData ? foundData.amount : 0;
    }));
    return combinedData as unknown as IFinancialSpreading;
  }));
  return getClearData(rawData, rawSheets);
}
