
'use strict';

const Service = require('egg').Service;
const moment = require('moment');
const _ = require('lodash');


class LoanCaculatorService extends Service {

    /**
     * 贷款计算器
     * @param {object} inputParams 
     */
    async getLoanCaculatorResult(inputParams) {
        const { ctx } = this;
        let type = inputParams.type;
        if (!['comercial', 'gjj', 'combination'].includes(type)) {
            ctx.failed('error type');
        }
        let ret = {
            debx: {},
            debj: {}
        }
        if (type === 'commercial') {
            inputParams.commercial.rate = (inputParams.commercial.lpr * 1000 + inputParams.commercial.basic_point * 100) / 1000 / 100;
            ret.debx = this.debx(inputParams.commercial);
            ret.debj = this.debj(inputParams.commercial);
        } else if (type === 'gjj') {
            inputParams.gjj.rate = inputParams.gjj.rate / 100;
            ret.debx = this.debx(inputParams.gjj);
            ret.debj = this.debj(inputParams.gjj);
        } else {
            inputParams.commercial.rate = (inputParams.commercial.lpr * 1000 + inputParams.commercial.basic_point * 100) / 1000 / 100;
            inputParams.gjj.rate = inputParams.gjj.rate / 100;
            let commercialDebx = this.debx(inputParams.commercial);
            let commercialDebj = this.debj(inputParams.commercial);
            let gjjDebx = this.debx(inputParams.gjj);
            let gjjDebj = this.debj(inputParams.gjj);

            //组合贷款将两部分加起来
            ret.debx = {
                month_repayment: Number((commercialDebx.month_repayment + gjjDebx.month_repayment).toFixed(2)),
                total_loan: Number((commercialDebx.total_loan + gjjDebx.total_loan).toFixed(2)),
                total_repayment: Number((commercialDebx.total_repayment + gjjDebx.total_repayment).toFixed(2)),
                total_interest: Number((commercialDebx.total_interest + gjjDebx.total_interest).toFixed(2)),
                total_years: inputParams.commercial.years,
                list: []
            }
            ret.debj = {
                month_repayment: Number((commercialDebj.month_repayment + gjjDebj.month_repayment).toFixed(2)),
                total_loan: Number((commercialDebj.total_loan + gjjDebj.total_loan).toFixed(2)),
                total_repayment: Number((commercialDebj.total_repayment + gjjDebj.total_repayment).toFixed(2)),
                total_interest: Number((commercialDebj.total_interest + gjjDebj.total_interest).toFixed(2)),
                total_years: inputParams.commercial.years,
                list: []
            }
            for (let i = 0; i < Number(inputParams.commercial.years * 12); i++) {
                ret.debx.list.push({
                    repayment: Number((commercialDebx.list[i].repayment + gjjDebx.list[i].repayment).toFixed(2)),
                    pricipal: Number((commercialDebx.list[i].pricipal + gjjDebx.list[i].pricipal).toFixed(2)),
                    interest: Number((commercialDebx.list[i].interest + gjjDebx.list[i].interest).toFixed(2)),
                    surplus: Number((commercialDebx.list[i].surplus + gjjDebx.list[i].surplus).toFixed(2)),
                });
                ret.debj.list.push({
                    repayment: Number((commercialDebj.list[i].repayment + gjjDebj.list[i].repayment).toFixed(2)),
                    pricipal: Number((commercialDebj.list[i].pricipal + gjjDebj.list[i].pricipal).toFixed(2)),
                    interest: Number((commercialDebj.list[i].interest + gjjDebj.list[i].interest).toFixed(2)),
                    surplus: Number((commercialDebj.list[i].surplus + gjjDebj.list[i].surplus).toFixed(2)),
                });
            }
        }
        return ret;
    }

    /**
     * 1.等额本息：
     * 月还款额=贷款本金*月利率*(1+月利率)^总还款月数/[(1+月利率)^总还款月数-1]。
     * 还款总额=还款月数×贷款额×月利率×(1+月利率)^贷款月数/〔(1+月利率)^还款月数- 1〕
     * 总利息=贷款本金×贷款月数×月利率×(1+月利率)^贷款月数/〔(1+月利率)^还款月数-1〕-贷款本金
     * 
     * 2.等额本金：
     * 月还款额=贷款本金/总还款月数+(贷款本金-累计已还款本金)*月利率
     * 还款总额=(还款月数+1)×贷款额×月利率/2+贷款本金
     * 总利息=(还款月数+1)×贷款额×月利率/2
     */

    /**
     * 等额本息
     * @param {*} inputParams 
     */
    debx(inputParams) {
        //贷款总额
        let totalLoan = inputParams.total * 10000;
        //贷款期数(月)
        let months = inputParams.years * 12;
        //月利率
        let monthRate = inputParams.rate / 12;
        //月供
        let monthRepayment = Number(((totalLoan * monthRate * Math.pow((1 + monthRate), months)) / (Math.pow(1 + monthRate, months) - 1)).toFixed(2));
        //还款总额
        let totalRepayment = Number(((months * totalLoan * monthRate * Math.pow((1 + monthRate), months)) / (Math.pow(1 + monthRate, months) - 1)).toFixed(2));
        //总利息
        let totalInterest = Number((totalRepayment - totalLoan).toFixed(2));
        /**
         * 等额本息还款法的利息计算：
         * 等额本息还贷，先算每月还贷本息：BX = a * i(1 + i) ^ N / [(1 + i) ^ N - 1]
         * 等额本息还贷第n个月还贷本金：
         * B = a * i(1 + i) ^ (n - 1) / [(1 + i) ^ N - 1]
         * 等额本息还贷第n个月还贷利息：
         * X = BX - B= a * i(1 + i) ^ N / [(1 + i) ^ N - 1] - a * i(1 + i) ^ (n - 1) / [(1 + i) ^ N - 1]
         * （注：BX = 等额本息还贷每月所还本金和利息总额，
         * B = 等额本息还贷每月所还本金，
         * a = 贷款总金额
         * i = 贷款月利率，
         * N = 还贷总月数，
         * n = 第n个月
         * X = 等额本息还贷每月所还的利息）
         */
        //计算每期的数据
        let list = [];
        let surplus = totalRepayment;
        for (let i = 1; i <= months; i++) {
            //最后一个月处理一下
            let monthRepaymentTemp = i === months ? surplus : monthRepayment;
            surplus = Number((surplus - monthRepaymentTemp).toFixed(2));
            //本月还贷本金
            let monthPricipal = Number((totalLoan * monthRate * Math.pow(1 + monthRate, i - 1) / (Math.pow(1 + monthRate, months) - 1)).toFixed(2));
            //本月还贷利息
            let monthInterest = Number((monthRepaymentTemp - monthPricipal).toFixed(2));
            list.push({
                repayment: monthRepaymentTemp,
                pricipal: monthPricipal,
                interest: monthInterest,
                surplus
            });
        }

        return {
            month_repayment: monthRepayment,
            total_loan: totalLoan,
            total_repayment: totalRepayment,
            total_interest: totalInterest,
            total_years: inputParams.years,
            list
        };
    }

    /**
     * 等额本金
     * @param {*} inputParams 
     */
    debj(inputParams) {
        //贷款总额
        let totalLoan = inputParams.total * 10000;
        //贷款期数(月)
        let months = inputParams.years * 12;
        //月利率
        let monthRate = inputParams.rate / 12;
        //首月月供
        let monthRepayment = Number((totalLoan / months + (totalLoan - 0) * monthRate).toFixed(2));
        //还款总额
        let totalRepayment = Number(((months + 1) * totalLoan * monthRate / 2 + totalLoan).toFixed(2));
        //总利息
        let totalInterest = totalRepayment - totalLoan;

        /**
         * 每月还本付息金额=（本金/还款月数）+（本金-累计已还本金）×月利率
         * 每月本金=总本金/还款月数
         * 每月利息=（本金-累计已还本金）×月利率
         * 还款总利息=（还款月数+1）×贷款额×月利率/2
         * 还款总额=（还款月数+1）×贷款额×月利率/2+ 贷款额
         * 注意：在等额本金法中，人们每月归还的本金额始终不变，利息随剩余本金的减少而减少，因而其每月还款额逐渐减少。
         */
        //计算每期的数据
        let list = [];
        //本月还贷本金
        let monthPricipal = Number((totalLoan / months).toFixed(2));
        let surplus = totalRepayment;
        for (let i = 1; i <= months; i++) {
            let monthRepaymentTemp = i === months ? surplus : Number((totalLoan / months + (totalLoan - monthPricipal * (i - 1)) * monthRate).toFixed(2));
            surplus = Number((surplus - monthRepaymentTemp).toFixed(2));
            //本月还贷利息
            let monthInterest = Number((monthRepaymentTemp - monthPricipal).toFixed(2));
            list.push({
                repayment: monthRepaymentTemp,
                pricipal: monthPricipal,
                interest: monthInterest,
                surplus
            });
        }

        return {
            month_repayment: monthRepayment,
            total_loan: totalLoan,
            total_repayment: totalRepayment,
            total_interest: totalInterest,
            total_years: inputParams.years,
            list
        };
    }

}

module.exports = LoanCaculatorService;
