import mongoose, { Model } from 'mongoose';
import Joi from 'joi';

import { MODEL_NAMES } from './_models';
import { BasicDTO } from '../helpers/db-data.helper';

export enum EFinancialSpreadingType {
  PROFIT_LOSS = 'PROFIT_LOSS',
  BALANCE_SHEET = 'BALANCE_SHEET',
}

export enum EFinanceSpreadingPLTotal {
  SALES = 'SALES',
  COST_OF_SALES = 'COST_OF_SALES',
  GROSS_PROFIT = 'GROSS_PROFIT',
  GROSS_MARGIN = 'GROSS_MARGIN',
  OPERATING_EXPENSES = 'OPERATING_EXPENSES',
  INCOME_FROM_OPERATIONS = 'INCOME_FROM_OPERATIONS',
  OPERATING_MARGIN = 'OPERATING_MARGIN',
  NON_OPERATING_EXPENSES = 'NON_OPERATING_EXPENSES',
  NON_OPERATING_INCOME = 'NON_OPERATING_INCOME',
  FINANCING_COSTS = 'FINANCING_COSTS',
  DEPRECIATION_AMORTIZATION = 'DEPRECIATION_AMORTIZATION',
  PRE_TAX_INCOME = 'PRE_TAX_INCOME',
  TAX = 'TAX',
  NET_INCOME = 'NET_INCOME',
}

export const financeSpreadingPLList: { [type in EFinanceSpreadingPLTotal]?: boolean } = {
  [EFinanceSpreadingPLTotal.SALES]: false,
  [EFinanceSpreadingPLTotal.COST_OF_SALES]: false,
  [EFinanceSpreadingPLTotal.GROSS_PROFIT]: true,
  [EFinanceSpreadingPLTotal.GROSS_MARGIN]: true,
  [EFinanceSpreadingPLTotal.OPERATING_EXPENSES]: false,
  [EFinanceSpreadingPLTotal.OPERATING_MARGIN]: true,
  [EFinanceSpreadingPLTotal.INCOME_FROM_OPERATIONS]: true,
  [EFinanceSpreadingPLTotal.NON_OPERATING_EXPENSES]: false,
  [EFinanceSpreadingPLTotal.NON_OPERATING_INCOME]: false,
  [EFinanceSpreadingPLTotal.FINANCING_COSTS]: false,
  [EFinanceSpreadingPLTotal.DEPRECIATION_AMORTIZATION]: false,
  [EFinanceSpreadingPLTotal.PRE_TAX_INCOME]: true,
  [EFinanceSpreadingPLTotal.TAX]: false,
  [EFinanceSpreadingPLTotal.NET_INCOME]: true,
};

export enum EFinanceSpreadingBSTotal {
  CASH_EQUIVALENTS = 'CASH_EQUIVALENTS',
  TRADE_RECEIVABLES = 'TRADE_RECEIVABLES',
  INVENTORY = 'INVENTORY',
  OTHER_CURRENT_ASSETS = 'OTHER_CURRENT_ASSETS',
  TOTAL_CURRENT_ASSET = 'TOTAL_CURRENT_ASSET',
  FIXED_ASSETS = 'FIXED_ASSETS',
  OTHER_NON_CURRENT_ASSETS = 'OTHER_NON_CURRENT_ASSETS',
  TOTAL_NON_CURRENT_ASSETS = 'TOTAL_NON_CURRENT_ASSETS',
  TOTAL_ASSETS = 'TOTAL_ASSETS',
  ACCOUNTS_PAYABLE = 'ACCOUNTS_PAYABLE',
  OTHER_CURRENT_LIABILITIES = 'OTHER_CURRENT_LIABILITIES',
  TOTAL_CURRENT_LIABILITIES = 'TOTAL_CURRENT_LIABILITIES',
  COMPANY_DEBT = 'COMPANY_DEBT',
  OTHER_DEBT = 'OTHER_DEBT',
  OTHER_LONG_TERM_LIABILITIES = 'OTHER_LONG_TERM_LIABILITIES',
  LONG_TERM_LIABILITIES = 'LONG_TERM_LIABILITIES',
  TOTAL_LIABILITIES = 'TOTAL_LIABILITIES',
  RETAINED_EARNINGS = 'RETAINED_EARNINGS',
  EQUITY_RESERVES = 'EQUITY_RESERVES',
  CHECK = 'CHECK'
}

export const financeSpreadingBSList: { [type in EFinanceSpreadingBSTotal]?: boolean } = {
  [EFinanceSpreadingBSTotal.CASH_EQUIVALENTS]: false,
  [EFinanceSpreadingBSTotal.TRADE_RECEIVABLES]: false,
  [EFinanceSpreadingBSTotal.INVENTORY]: false,
  [EFinanceSpreadingBSTotal.OTHER_CURRENT_ASSETS]: false,
  [EFinanceSpreadingBSTotal.TOTAL_CURRENT_ASSET]: true,
  [EFinanceSpreadingBSTotal.FIXED_ASSETS]: false,
  [EFinanceSpreadingBSTotal.OTHER_NON_CURRENT_ASSETS]: false,
  [EFinanceSpreadingBSTotal.TOTAL_NON_CURRENT_ASSETS]: true,
  [EFinanceSpreadingBSTotal.TOTAL_ASSETS]: true,
  [EFinanceSpreadingBSTotal.ACCOUNTS_PAYABLE]: false,
  [EFinanceSpreadingBSTotal.OTHER_CURRENT_LIABILITIES]: false,
  [EFinanceSpreadingBSTotal.TOTAL_CURRENT_LIABILITIES]: true,
  [EFinanceSpreadingBSTotal.COMPANY_DEBT]: false,
  [EFinanceSpreadingBSTotal.OTHER_DEBT]: false,
  [EFinanceSpreadingBSTotal.OTHER_LONG_TERM_LIABILITIES]: false,
  [EFinanceSpreadingBSTotal.LONG_TERM_LIABILITIES]: true,
  [EFinanceSpreadingBSTotal.TOTAL_LIABILITIES]: true,
  [EFinanceSpreadingBSTotal.RETAINED_EARNINGS]: false,
  [EFinanceSpreadingBSTotal.EQUITY_RESERVES]: false,
  [EFinanceSpreadingBSTotal.CHECK]: true,
};

export const financialSpreadingTotalDictionary: Record<EFinanceSpreadingPLTotal | EFinanceSpreadingBSTotal, string> = {
  [EFinanceSpreadingPLTotal.SALES]: 'Sales',
  [EFinanceSpreadingPLTotal.COST_OF_SALES]: 'Cost of Sales',
  [EFinanceSpreadingPLTotal.GROSS_PROFIT]: 'Gross Profit',
  [EFinanceSpreadingPLTotal.GROSS_MARGIN]: 'Gross Margin',
  [EFinanceSpreadingPLTotal.OPERATING_EXPENSES]: 'Operating Expenses',
  [EFinanceSpreadingPLTotal.INCOME_FROM_OPERATIONS]: 'Income from Operations',
  [EFinanceSpreadingPLTotal.OPERATING_MARGIN]: 'Operating Margin',
  [EFinanceSpreadingPLTotal.NON_OPERATING_EXPENSES]: 'Non operating Expenses',
  [EFinanceSpreadingPLTotal.NON_OPERATING_INCOME]: 'Non operating Income',
  [EFinanceSpreadingPLTotal.FINANCING_COSTS]: 'Financing Costs',
  [EFinanceSpreadingPLTotal.DEPRECIATION_AMORTIZATION]: 'Depreciation & Amortization',
  [EFinanceSpreadingPLTotal.PRE_TAX_INCOME]: 'Pre-Tax Income',
  [EFinanceSpreadingPLTotal.TAX]: 'Tax',
  [EFinanceSpreadingPLTotal.NET_INCOME]: 'Net Income',
  [EFinanceSpreadingBSTotal.CASH_EQUIVALENTS]: 'Cash & Equivalents',
  [EFinanceSpreadingBSTotal.TRADE_RECEIVABLES]: 'Trade Receivables',
  [EFinanceSpreadingBSTotal.INVENTORY]: 'Inventory',
  [EFinanceSpreadingBSTotal.OTHER_CURRENT_ASSETS]: 'Other Current Assets',
  [EFinanceSpreadingBSTotal.TOTAL_CURRENT_ASSET]: 'Total Current Assets',
  [EFinanceSpreadingBSTotal.FIXED_ASSETS]: 'Fixed Assets',
  [EFinanceSpreadingBSTotal.OTHER_NON_CURRENT_ASSETS]: 'Other Non Current Assets',
  [EFinanceSpreadingBSTotal.TOTAL_NON_CURRENT_ASSETS]: 'Total Non Current Assets',
  [EFinanceSpreadingBSTotal.ACCOUNTS_PAYABLE]: 'Accounts Payable',
  [EFinanceSpreadingBSTotal.OTHER_CURRENT_LIABILITIES]: 'Other Current Liabilities',
  [EFinanceSpreadingBSTotal.TOTAL_CURRENT_LIABILITIES]: 'Total Current Liabilities',
  [EFinanceSpreadingBSTotal.COMPANY_DEBT]: 'Senior Debt',
  [EFinanceSpreadingBSTotal.OTHER_DEBT]: 'Other Debt',
  [EFinanceSpreadingBSTotal.OTHER_LONG_TERM_LIABILITIES]: 'Other Long Term Liabilities',
  [EFinanceSpreadingBSTotal.LONG_TERM_LIABILITIES]: 'Long Term Liabilities',
  [EFinanceSpreadingBSTotal.TOTAL_ASSETS]: 'Total Assets',
  [EFinanceSpreadingBSTotal.TOTAL_LIABILITIES]: 'Total Liabilities',
  [EFinanceSpreadingBSTotal.RETAINED_EARNINGS]: 'Retained Earnings',
  [EFinanceSpreadingBSTotal.EQUITY_RESERVES]: 'Equity & Reserves',
  [EFinanceSpreadingBSTotal.CHECK]: 'Check',
};

export const financeSpreadingLists = {
  [EFinancialSpreadingType.PROFIT_LOSS]: financeSpreadingPLList,
  [EFinancialSpreadingType.BALANCE_SHEET]: financeSpreadingBSList,
};

export interface IFinancialSpreadingSheetView {
  _id: string;
  name: string;
  borrowerId: string;
  financialSpreadingType: string;
  rowType: string;
  order: number;
  suborder: number;
  isTotal: boolean;
  isCalculated: boolean;
  isFixedCost?: boolean;
}

export class FinancialSpreadingSheetDTO extends BasicDTO {
  borrowerId: string;
  financialSpreadingType: EFinancialSpreadingType;
  rowType: EFinanceSpreadingPLTotal | EFinanceSpreadingBSTotal;
  name: string;
  order: number;
  suborder: number;
  isTotal: boolean;
  isCalculated: boolean;
  isFixedCost?: boolean;
}

export const financialSpreadingSheetViewValidationSchema = Joi.object<IFinancialSpreadingSheetView>({
  _id: Joi.string().required(),
  name: Joi.string().required(),
  borrowerId: Joi.string().required(),
  financialSpreadingType: Joi.string().required(),
  rowType: Joi.string().required(),
  order: Joi.number().integer().required(),
  suborder: Joi.number().integer().required(),
  isTotal: Joi.boolean().required(),
  isCalculated: Joi.boolean().required(),
  isFixedCost: Joi.boolean().allow(null),
});

export interface IFinancialSpreadingSheet {
  borrowerId: mongoose.Types.ObjectId;
  financialSpreadingType: EFinancialSpreadingType;
  rowType: EFinanceSpreadingPLTotal | EFinanceSpreadingBSTotal;
  name: string;
  order: number;
  suborder: number;
  isTotal: boolean;
  isCalculated: boolean;
  isFixedCost?: boolean;
}

export interface IFinancialSpreadingSheetDoc extends IFinancialSpreadingSheet, Document {
  _id: mongoose.Types.ObjectId;
  createdAt: Date;
  updatedAt: Date;
}

export interface IFinancialSpreadingSheetLean extends IFinancialSpreadingSheet {
  _id: mongoose.Types.ObjectId;
  createdAt: Date;
  updatedAt: Date;
}

export interface IFinancialSpreadingSheetPlain extends Omit<IFinancialSpreadingSheet, 'borrowerId'> {
  _id: string;
  borrowerId: string;
  createdAt: Date;
  updatedAt: Date;
}

export type TFinancialSpreadingSheetModel = Model<IFinancialSpreadingSheet, {}, {}>;

export const FinancialSpreadingSheetSchema = new mongoose.Schema<IFinancialSpreadingSheet, TFinancialSpreadingSheetModel>(
  {
    borrowerId: {
      type: mongoose.Schema.Types.ObjectId,
      ref: MODEL_NAMES.borrowers,
      required: true,
    },
    financialSpreadingType: {
      type: String,
      enum: Object.values(EFinancialSpreadingType),
      required: true,
    },
    rowType: {
      type: String,
      enum: [...Object.values(EFinanceSpreadingPLTotal), ...Object.values(EFinanceSpreadingBSTotal)],
      required: true,
      validate: {
        validator: function(this: any, value: string) {
          if (this.financialSpreadingType === EFinancialSpreadingType.PROFIT_LOSS && !(value in EFinanceSpreadingPLTotal)) {
            return false;
          }

          if (this.financialSpreadingType === EFinancialSpreadingType.BALANCE_SHEET && !(value in EFinanceSpreadingBSTotal)) {
            return false;
          }

          return true;
        },
        message: 'Invalid rowType for the provided financialSpreadingType',
      },
    },
    name: {
      type: String,
      required: true,
    },
    order: {
      type: Number,
      required: true,
    },
    suborder: {
      type: Number,
      required: true,
    },
    isTotal: {
      type: Boolean,
      required: true,
    },
    isCalculated: {
      type: Boolean,
      required: true,
    },
    isFixedCost: {
      type: Boolean,
      default: false,
    },
  },
  {
    timestamps: { createdAt: true, updatedAt: true },
  },
);

export const FinancialSpreadingSheet = mongoose.model<IFinancialSpreadingSheet, TFinancialSpreadingSheetModel>(MODEL_NAMES.financialSpreadingSheets, FinancialSpreadingSheetSchema);
