# -*- coding: utf-8 -*-
import sys
import os
import numpy as np
import datetime
import pandas as pd
from decimal import Decimal, ROUND_HALF_UP

#path = '/home/quant_group/vpants/finance/table/'
path = '/Users/chenxiaozhe/Downloads/test/201901'
sys.path.append('../../../finance_out_table')
from db_con.db_connect import sql_engine

step = 5000
engine_audit = sql_engine('audit_w', 'new_transaction').get_engine()
engine_xyqb = sql_engine('xyqb', 'xyqb').get_engine()

read_user_sql_time = '''
select t1.id,t1.ref_id,t1.real_loan_id loan_id,t1.fund_code,t2.contract_created_at,t1.term_no,t1.deadline,t1.xyqb_repaid_at repaid_at,
t1.principle cur_principle,t1.interest cur_interest,t2.monthly_interest_rate rate,t2.contract_term,t2.contract_loan_amount contract_amount
from new_transaction.user_repayment_plan t1
join audit.loan_manifest t2 on t1.ref_id = t2.ref_id
where date(t1.deadline) < date(t1.xyqb_repaid_at) AND date(t1.xyqb_repaid_at) >= '%s' and date(t1.xyqb_repaid_at) < '%s'
'''

#=== 指定了 plan_id 的重新计算罚息
read_user_sql_id = '''
select t1.id,t1.ref_id,t1.real_loan_id loan_id,t1.fund_code,t2.contract_created_at,t1.term_no,t1.deadline,t1.xyqb_repaid_at repaid_at,
t1.principle cur_principle,t1.interest cur_interest,t2.monthly_interest_rate rate,t2.contract_term,t2.contract_loan_amount contract_amount
from new_transaction.user_repayment_plan t1
left join audit.loan_manifest t2 on t1.ref_id = t2.ref_id
where t1.id in %s
'''

#===计算剩余本金
sql_query_surplus_principle = """
SELECT ref_id,SUM(principle) surplus_prin FROM new_transaction.user_repayment_plan WHERE term_no>=%s AND ref_id in %s 
group by 1
"""
#== 更新罚息
update_sql = '''
update user_repayment_plan set should_punish = %s where id = %s
'''

#== 信用钱包 repayment_record 直接使用这个中的 罚息值
sql_xyqb='''
select loan_application_history_id loan_id,term_no, overdue_service_fee+overdue_interest as 'overdue' from repayment_record 
where loan_application_history_id in %s and term_no = %s 
'''

class plan_punish():
    def __init__(self, is_mock):
        self.is_mock = is_mock
        today = datetime.date.today()
        self.end = datetime.date(today.year, today.month, 1)
        last_day = self.end + datetime.timedelta(days=-1)
        self.start = datetime.date(last_day.year, last_day.month, 1)
        self.dir_path = path + self.start.strftime('%Y%m')
        if not os.path.exists(self.dir_path):
            os.makedirs(self.dir_path)

    def cal_fund_fee(self, deadline, repaid_at, surplus_prin, cur_principle, cur_interest, rate, fund_code):
        result = {'lkb_fund_fee': 0}
        lkb_deplay_days = (repaid_at.date() - deadline.date()).days
        if lkb_deplay_days < 1:
            return result
        if fund_code == 290:
            # 湖北逾期3天内免息
            if lkb_deplay_days <= 3:
                return result
            # 湖北27号代偿 逾期天数不在增加。
            year = deadline.year
            month = deadline.month
            day = deadline.day
            # 最大逾期天数
            max_deplay_days = lkb_deplay_days
            if day >= 27:
                max_deplay_days = (datetime.date(year, month + 1, 27) - deadline.date()).days
            else:
                max_deplay_days = (datetime.date(year, month, 27) - deadline.date()).days
            # 限制最大逾期天数
            if lkb_deplay_days > max_deplay_days:
                lkb_deplay_days = max_deplay_days
            # 湖北罚息
            result['lkb_fund_fee'] = np.round(cur_principle * lkb_deplay_days * (rate * 12 / 360), 2)
        elif fund_code in (350, 380):
            # 代偿为T+20
            # 众网小贷罚息 逾期本息*0.0007
            if lkb_deplay_days > 20:
                lkb_deplay_days = 20
            result['lkb_fund_fee'] = np.round((cur_principle + cur_interest) * 0.0007 * lkb_deplay_days, 2)
        elif fund_code == 420:
            #    惠金所罚息 剩余本金*0.01
            # surplus_principle = cal_surplus_prin(contract_amount, term_no, contract_term, rate,ref_id)
            result['lkb_fund_fee'] = np.round(surplus_prin * 0.01, 2)
        elif fund_code == 460:
            result['lkb_fund_fee'] = np.round((cur_principle + cur_interest) * 0.001 * lkb_deplay_days, 2)
        return result

    def cal_deplay_fee(self, contract_amount, deadline, repaid_at, contract_created_at, surplus_prin, cur_principle):
        result = {'lkb_deplay_fee': 0}
        lkb_deplay_days = (repaid_at.date() - deadline.date()).days
        if lkb_deplay_days < 1:
            return result
        if (repaid_at >= datetime.datetime(2017, 1, 20, 10, 0, 0)) and (
                repaid_at < datetime.datetime(2017, 5, 2, 14, 16, 0)):  # 2017-1-20 号修改了罚息计算公式
            principle = surplus_prin
            result['lkb_deplay_fee'] = np.round(contract_amount * 0.01 + principle * 0.005 * lkb_deplay_days, 2)
            return result
        elif (repaid_at >= datetime.datetime(2017, 5, 2, 14, 16, 0)) and (
                repaid_at < datetime.datetime(2017, 6, 23, 19, 7, 0)):  # 2017-5-2 修改了罚息公式  当期逾期服务费不超过当期借款本金的20%
            principle = surplus_prin
            result['lkb_deplay_fee'] = np.round(contract_amount * 0.01 + principle * 0.005 * lkb_deplay_days, 2)
            # 直接使用还款计划的应还本金
            if result['lkb_deplay_fee'] > cur_principle * 0.2:
                result['lkb_deplay_fee'] = np.round(cur_principle * 0.2, 2)
            return result
        elif repaid_at >= datetime.datetime(2017, 6, 23, 19, 7, 0) and contract_created_at < datetime.datetime(2017, 11, 14, 15, 35,36):  # 2017-6-23 修改了罚息公式,当期 罚息不超过 借款本金的20%
            principle = surplus_prin
            puinsh_fee = Decimal(
                str(np.round(contract_amount * 0.01 + principle * 0.005 * lkb_deplay_days, 10))).quantize(
                Decimal('1.00'), rounding=ROUND_HALF_UP)
            result['lkb_deplay_fee'] = round(puinsh_fee, 2)
            if result['lkb_deplay_fee'] > contract_amount * 0.2:
                result['lkb_deplay_fee'] = np.round(contract_amount * 0.2, 2)
            return result
        elif contract_created_at >= datetime.datetime(2017, 11, 14, 15, 35, 36) and contract_created_at < datetime.datetime(2017, 12, 12, 15, 26, 0):
            # ==剩余本金 http://192.168.3.8/pages/viewpage.action?pageId=5246700
            principle = surplus_prin
            puinsh_fee = Decimal(str(np.round(principle * 0.0015 * lkb_deplay_days, 10))).quantize(Decimal('1.00'),
                                                                                                   rounding=ROUND_HALF_UP)
            result['lkb_deplay_fee'] = max(round(puinsh_fee, 2), 20)
            return result
        elif contract_created_at >= datetime.datetime(2017, 12, 12, 15, 26, 0):
            # ==当期本金  http://192.168.3.8/pages/viewpage.action?pageId=5246700
            puinsh_fee = Decimal(str(np.round(cur_principle * 0.001 * lkb_deplay_days, 10))).quantize(Decimal('1.00'), rounding=ROUND_HALF_UP)
            result['lkb_deplay_fee'] = min(round(puinsh_fee, 2), contract_amount * 0.2)
            return result
        return result

    def plan_punish_details(self, plan_ids):
        _sql = None
        if self.start and self.end:  # == 时间
            _sql = read_user_sql_time % (self.start, self.end)
        if plan_ids:
            plan_ids = list(plan_ids)
            _sql = read_user_sql_id % str(tuple(plan_ids)).replace(',)', ')')
        if _sql:
            df_gen = pd.read_sql(sql=_sql, con=engine_audit, chunksize=step)
            for df in df_gen:
                df.fillna(0, inplace=True)
                df_bad = df.ix[(df.contract_term == 0)]
                if len(df_bad) > 0:
                    df_bad.to_csv('df_bad_%s.csv', index=None, mode='a')
                df_good = df.ix[(df.contract_term != 0)]
                df_good.ref_id = df_good.ref_id.astype(int)
                df_good_new = pd.DataFrame()
                # ==查询剩余本金
                max_term_no = max(df.term_no.unique())
                for i in range(1, max_term_no + 1, 1):
                    df_tmp = df_good.ix[df_good.term_no == i]
                    if len(df_tmp) == 0:
                        continue
                    # ==剩余未还本息
                    df_surplus_principle = pd.read_sql(
                        sql_query_surplus_principle % (i, str(tuple(df_tmp.ref_id.tolist())).replace(',)', ')')),
                        engine_audit)
                    df_surplus_principle.ref_id = df_surplus_principle.ref_id.astype(int)
                    df_tmp = pd.merge(df_tmp, df_surplus_principle, on='ref_id', how='left')
                    df_tmp.term_no = df_tmp.term_no.astype(int)
                    # #== 查询 xyqb 罚息

                    df_tmp = df_tmp.loc[~df_tmp.loan_id.str.contains("o")]

                    df_xyqb = pd.read_sql(
                        sql_xyqb % (str(tuple(df_tmp.loan_id.astype(int).tolist())).replace(',)', ')'), i), engine_xyqb)

                    df_xyqb.loan_id = df_xyqb.loan_id.astype(int)
                    df_xyqb.term_no = df_xyqb.term_no.astype(int)
                    df_xyqb['overdue'].fillna(0, inplace=True)
                    # ==合并信用钱包的罚息
                    df_tmp.loan_id = df_tmp.loan_id.astype(int)
                    df_tmp = pd.merge(df_tmp, df_xyqb, how='left')
                    # ==直接使用 信用钱包 repayment_record 中的罚息字段，进行更新罚息
                    df_update = df_tmp.loc[df_tmp.loan_id.isin(df_xyqb.loan_id)]
                    # == 0 的情况，直接使用罚息计算公式计算，原因部分还款
                    df_zero = df_update.loc[df_update['overdue'] == 0.0]
                    df_update = df_update.loc[df_update['overdue'] != 0.0]
                    if len(df_update) > 0:
                        up_val = df_update[['overdue', 'id']].values.tolist()
                        if not self.is_mock:
                            engine_audit.execute(update_sql, up_val)
                        else:
                            print(up_val)
                    # == 非 repayment_record 中的记录，需要进行计算罚息,如果罚息为0，则使用公式进行计算
                    df_good_new = df_good_new.append(df_tmp.loc[~df_tmp.loan_id.isin(df_xyqb.loan_id)])
                    df_good_new = df_good_new.append(df_zero)
                if len(df_good_new) > 0:
                    df_good_new['surplus_prin'].fillna(0, inplace=True)
                    ## contract_amount, deadline, repaid_at, contract_created_at, surplus_prin, cur_principle
                    df_good_new['should_punish'] = df_good_new[
                        ['contract_amount', 'deadline', 'repaid_at', 'contract_created_at', 'surplus_prin',
                         'cur_principle']].apply(lambda x: self.cal_deplay_fee(**dict(x))['lkb_deplay_fee'], axis=1)
                    ## deadline, repaid_at, surplus_prin, cur_principle, cur_interest,  rate,fund_code
                    df_good_new['fund_punish'] = df_good_new[
                        ['deadline', 'repaid_at', 'surplus_prin', 'cur_principle', 'cur_interest', 'rate',
                         'fund_code']].apply(lambda x: self.cal_fund_fee(**dict(x))['lkb_fund_fee'], axis=1)
                    ## 包括了 量化派罚息和资金方罚息
                    df_good_new['should_punish'] = df_good_new['should_punish'] + df_good_new['fund_punish']
                    # 更新数据库
                    df_good_new['id'] = df_good_new['id'].map('{:.0f}'.format)
                    df_good_new['should_punish'] = df_good_new['should_punish'].apply(lambda x: round(x, 2))
                    up_val = df_good_new[['should_punish', 'id']].values.tolist()
                    if not self.is_mock:
                        engine_audit.execute(update_sql, up_val)
                    else:
                        print(up_val)


if __name__ == '__main__':
    plan_punish(False).plan_punish_details(plan_ids=None)
    print('======================plan_punish done=================================== time:%s' % datetime.datetime.today())
