# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt

from __future__ import unicode_literals
import frappe, erpnext
from frappe import _
from frappe.model.document import Document
from frappe.utils import (nowdate, getdate, now_datetime, get_datetime, flt, date_diff, get_last_day, cint,
	get_first_day, get_datetime, add_days)
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.accounts.general_ledger import make_gl_entries

class LoanInterestAccrual(AccountsController):
	def validate(self):
		if not self.loan:
			frappe.throw(_("Loan is mandatory"))

		if not self.posting_date:
			self.posting_date = nowdate()

		if not self.interest_amount and not self.payable_principal_amount:
			frappe.throw(_("Interest Amount or Principal Amount is mandatory"))


	def on_submit(self):
		self.make_gl_entries()

	def on_cancel(self):
		if self.repayment_schedule_name:
			self.update_is_accrued()

		self.make_gl_entries(cancel=1)
		self.ignore_linked_doctypes = ['GL Entry']

	def update_is_accrued(self):
		frappe.db.set_value('Repayment Schedule', self.repayment_schedule_name, 'is_accrued', 0)

	def make_gl_entries(self, cancel=0, adv_adj=0):
		gle_map = []

		if self.interest_amount:
			gle_map.append(
				self.get_gl_dict({
					"account": self.loan_account,
					"party_type": self.applicant_type,
					"party": self.applicant,
					"against": self.interest_income_account,
					"debit": self.interest_amount,
					"debit_in_account_currency": self.interest_amount,
					"against_voucher_type": "Loan",
					"against_voucher": self.loan,
					"remarks": _("Against Loan:") + self.loan,
					"cost_center": erpnext.get_default_cost_center(self.company),
					"posting_date": self.posting_date
				})
			)

			gle_map.append(
				self.get_gl_dict({
					"account": self.interest_income_account,
					"party_type": self.applicant_type,
					"party": self.applicant,
					"against": self.loan_account,
					"credit": self.interest_amount,
					"credit_in_account_currency":  self.interest_amount,
					"against_voucher_type": "Loan",
					"against_voucher": self.loan,
					"remarks": _("Against Loan:") + self.loan,
					"cost_center": erpnext.get_default_cost_center(self.company),
					"posting_date": self.posting_date
				})
			)

		if gle_map:
			make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj)


# For Eg: If Loan disbursement date is '01-09-2019' and disbursed amount is 1000000 and
# rate of interest is 13.5 then first loan interest accural will be on '01-10-2019'
# which means interest will be accrued for 30 days which should be equal to 11095.89
def calculate_accrual_amount_for_demand_loans(loan, posting_date, process_loan_interest):
	no_of_days = get_no_of_days_for_interest_accural(loan, posting_date)

	if no_of_days <= 0:
		return

	if loan.status == 'Disbursed':
		pending_principal_amount = flt(loan.total_payment) - flt(loan.total_interest_payable) \
			- flt(loan.total_principal_paid)
	else:
		pending_principal_amount = loan.disbursed_amount

	interest_per_day = (pending_principal_amount * loan.rate_of_interest) / (days_in_year(get_datetime(posting_date).year) * 100)
	payable_interest = interest_per_day * no_of_days

	args = frappe._dict({
		'loan': loan.name,
		'applicant_type': loan.applicant_type,
		'applicant': loan.applicant,
		'interest_income_account': loan.interest_income_account,
		'loan_account': loan.loan_account,
		'pending_principal_amount': pending_principal_amount,
		'interest_amount': payable_interest,
		'process_loan_interest': process_loan_interest,
		'posting_date': posting_date
	})

	make_loan_interest_accrual_entry(args)

def make_accrual_interest_entry_for_demand_loans(posting_date, process_loan_interest, open_loans=None, loan_type=None):
	query_filters = {
		"status": ('in', ['Disbursed', 'Partially Disbursed']),
		"docstatus": 1
	}

	if loan_type:
		query_filters.update({
			"loan_type": loan_type
		})

	if not open_loans:
		open_loans = frappe.get_all("Loan",
			fields=["name", "total_payment", "total_amount_paid", "loan_account", "interest_income_account",
				"is_term_loan", "status", "disbursement_date", "disbursed_amount", "applicant_type", "applicant",
				"rate_of_interest", "total_interest_payable", "total_principal_paid", "repayment_start_date"],
			filters=query_filters)

	for loan in open_loans:
		calculate_accrual_amount_for_demand_loans(loan, posting_date, process_loan_interest)

def make_accrual_interest_entry_for_term_loans(posting_date, process_loan_interest, term_loan=None, loan_type=None):
	curr_date = posting_date or add_days(nowdate(), 1)

	term_loans = get_term_loans(curr_date, term_loan, loan_type)

	accrued_entries = []

	for loan in term_loans:
		accrued_entries.append(loan.payment_entry)
		args = frappe._dict({
			'loan': loan.name,
			'applicant_type': loan.applicant_type,
			'applicant': loan.applicant,
			'interest_income_account': loan.interest_income_account,
			'loan_account': loan.loan_account,
			'interest_amount': loan.interest_amount,
			'payable_principal': loan.principal_amount,
			'process_loan_interest': process_loan_interest,
			'repayment_schedule_name': loan.payment_entry,
			'posting_date': posting_date
		})

		make_loan_interest_accrual_entry(args)

	if accrued_entries:
		frappe.db.sql("""UPDATE `tabRepayment Schedule`
			SET is_accrued = 1 where name in (%s)""" #nosec
			% ", ".join(['%s']*len(accrued_entries)), tuple(accrued_entries))

def get_term_loans(date, term_loan=None, loan_type=None):
	condition = ''

	if term_loan:
		condition +=' AND l.name = %s' % frappe.db.escape(term_loan)

	if loan_type:
		condition += ' AND l.loan_type = %s' % frappe.db.escape(loan_type)

	term_loans = frappe.db.sql("""SELECT l.name, l.total_payment, l.total_amount_paid, l.loan_account,
			l.interest_income_account, l.is_term_loan, l.disbursement_date, l.applicant_type, l.applicant,
			l.rate_of_interest, l.total_interest_payable, l.repayment_start_date, rs.name as payment_entry,
			rs.payment_date, rs.principal_amount, rs.interest_amount, rs.is_accrued , rs.balance_loan_amount
			FROM `tabLoan` l, `tabRepayment Schedule` rs
			WHERE rs.parent = l.name
			AND l.docstatus=1
			AND l.is_term_loan =1
			AND rs.payment_date <= %s
			AND rs.is_accrued=0 {0}
			AND l.status = 'Disbursed'""".format(condition), (getdate(date)), as_dict=1)

	return term_loans

def make_loan_interest_accrual_entry(args):
	precision = cint(frappe.db.get_default("currency_precision")) or 2

	loan_interest_accrual = frappe.new_doc("Loan Interest Accrual")
	loan_interest_accrual.loan = args.loan
	loan_interest_accrual.applicant_type = args.applicant_type
	loan_interest_accrual.applicant = args.applicant
	loan_interest_accrual.interest_income_account = args.interest_income_account
	loan_interest_accrual.loan_account = args.loan_account
	loan_interest_accrual.pending_principal_amount = flt(args.pending_principal_amount, precision)
	loan_interest_accrual.interest_amount = flt(args.interest_amount, precision)
	loan_interest_accrual.posting_date = args.posting_date or nowdate()
	loan_interest_accrual.process_loan_interest_accrual = args.process_loan_interest
	loan_interest_accrual.repayment_schedule_name = args.repayment_schedule_name
	loan_interest_accrual.payable_principal_amount = args.payable_principal

	loan_interest_accrual.save()
	loan_interest_accrual.submit()


def get_no_of_days_for_interest_accural(loan, posting_date):
	last_interest_accrual_date = get_last_accural_date_in_current_month(loan)

	no_of_days = date_diff(posting_date or nowdate(), last_interest_accrual_date) + 1

	return no_of_days

def get_last_accural_date_in_current_month(loan):
	last_posting_date = frappe.db.sql(""" SELECT MAX(posting_date) from `tabLoan Interest Accrual`
		WHERE loan = %s""", (loan.name))

	if last_posting_date[0][0]:
		# interest for last interest accrual date is already booked, so add 1 day
		return add_days(last_posting_date[0][0], 1)
	else:
		return loan.disbursement_date

def days_in_year(year):
	days = 365

	if (year % 4 == 0) and (year % 100 != 0) or (year % 400 == 0):
		days = 366

	return days

