import mongoose from 'mongoose';
import Decimal from 'decimal.js';

import { getBBCDateDoc } from '../db/bbcDates.db';
import { EReserveTypes } from '../enums/reserve-types.enum';

import { IReserve, IReserveItem, IReserveSummary } from '../models/Reserve.model';
import { copyReserveWithoutSaving, getLastReserveDocs, getReserveDocs, saveReserveDoc } from '../db/reserve.db';
import { SignsService } from './signs.service';

export class ReserveService {

  constructor(
    private readonly signsService: SignsService,
  ) {
  }

  async getReserve(bbcDateId: string): Promise<IReserve> {
    const bbcDateIdID = new mongoose.Types.ObjectId(bbcDateId);
    const reserves = await getReserveDocs([bbcDateIdID]);
    if (!!reserves.length) {
      return reserves.pop();
    }
    const lastItems = await this.getLastCopyReserve(bbcDateId);
    const isBBCLockedResult = await this.signsService.isBBCLocked(bbcDateId);
    if (isBBCLockedResult) {
      const summary = this.calculateSummary(lastItems);
      return await copyReserveWithoutSaving(bbcDateIdID, lastItems, summary);
    }
    return this.saveAndReturnReserve(bbcDateId, { items: lastItems, notes: '' });
  }

  async saveAndReturnReserve(bbcDateId: string, reserveUpdate: Partial<IReserve>) {
    const isBBCLockedResult = await this.signsService.isBBCLocked(bbcDateId);
    if (isBBCLockedResult) {
      return null;
    }
    const summary = this.calculateSummary(reserveUpdate.items);
    return await saveReserveDoc(new mongoose.Types.ObjectId(bbcDateId), { ...reserveUpdate, summary });
  };

  async getLastCopyReserve(bbcDateId: string) {
    const { borrowerId } = await getBBCDateDoc(bbcDateId.toString());
    const equipment = await getLastReserveDocs(bbcDateId, borrowerId.toString());
    if (!!equipment.length) {
      return equipment.pop().items;
    }
    return [];
  }

  private calculateSummary(reserveItems: IReserveItem[]): IReserveSummary {
    const summary: { rowDef: string, amount: number }[] = [];
    const summaryOrder = [
      EReserveTypes.RENT,
      EReserveTypes.PAYROLL,
      EReserveTypes.INSURANCE,
      EReserveTypes.TAX,
      EReserveTypes.INVENTORY,
      EReserveTypes.RECEIVABLES,
      EReserveTypes.EQUIPMENT,
      EReserveTypes.OTHER,
    ];
    const result = Object.keys(EReserveTypes).map((equipmentType) => {
      const typeSummary = { rowDef: equipmentType, amount: 0 };
      reserveItems.forEach((item) => {
        if (item.type === equipmentType) {
          typeSummary.amount = new Decimal(typeSummary.amount).add(item.amount ?? 0).toNumber();
        }
        return typeSummary;
      });
      return typeSummary;
    });
    summaryOrder.forEach((summaryType) => {
      summary.push(...result.filter((r) => r.rowDef === summaryType));
    });
    return summary.reduce((acc, s) => ({ ...acc, [s.rowDef]: s }), {});
  }

}
