# -*- coding: utf-8 -*-

# from api.db_connect import *
import numpy as np
import datetime
import pandas as pd
import sys
from sqlalchemy import create_engine

reload(sys)
sys.setdefaultencoding('utf8')

'''
更新数据库中的 应还罚息
@author linfang
1、更新 user_repayment_plan 表中的 should_punish 字段
2、罚息计算，是还款成功（即还款状态为 3）才进行计算，如果还款未完成，则不应该计算罚息。
'''

# t2.funding_code in (290,380,350,420) AND
# ----------------------配置变量开始---------------------------
# TODO 查询计算罚息的还款计划，如果要更新哪个放款，则指定具体的放款id或还款时间段等其他你需要的条件，但是请别删除查询项
read_user_sql = '''
select t1.id,t1.fund_code,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.repay_status = 3 and date(t1.deadline) < date(t1.xyqb_repaid_at) and
t1.xyqb_repaid_at >= '2017-10-01 00:00:00' and t1.xyqb_repaid_at < '2017-11-10 00:00:00'
'''

# read_user_sql='''
# select t1.id,t1.fund_code,t1.term_no,t1.deadline,t1.xyqb_repaid_at repaid_at,t1.principle cur_principle,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.repay_status = 3 and date(t1.deadline) < date(t1.xyqb_repaid_at) and
# t1.real_loan_id = '38517285'
# '''
# TODO 这个字段一定要注意；如果不进行更新表中的数据，请设置为 False ！！！！！！
# TODO False 表示输出到控制台
update_flag = True
# TODO 数据源
engine_audit = create_engine('mysql+mysqldb://bo.wang:#mn89453YU9io80@172.16.3.201:3306/new_transaction?charset=utf8',
                             echo=True)
# ----------------------配置变量结束---------------------------




'''
晋商的剩余本金计算特殊区别
剩余本金计算
contract_amount：放款金额
term_no：期数
contract_term：合同期数
rate：利率
'''


def cal_surplus_prin(contract_amount, term_no, contract_term, rate, fund_code=0):
    prin = 0
    if fund_code == 300:
        avg = round(contract_amount / contract_term, 2)
        return contract_amount - avg * (term_no - 1)
    else:
        for i in xrange(1, term_no, 1):
            prin += np.ppmt(rate, i, contract_term, -contract_amount)
        return contract_amount - prin


# 计算当期本金
def cal_cur_prin(contract_amount, term_no, contract_term, rate, fund_code=0):
    prin = 0
    if fund_code == 300:
        avg = round(contract_amount / contract_term, 2)
        if term_no < contract_term:
            return avg
        else:
            return contract_amount - avg * (term_no - 1)
    else:
        if contract_term == 1:
            return contract_amount
        else:
            if term_no < contract_term:
                return np.ppmt(rate, term_no, contract_term, -contract_amount)
            else:  # 最后一期
                other = 0
                for i in range(1, contract_term):
                    other += np.ppmt(rate, i, contract_term, -contract_amount)
                return contract_amount - other


'''
剩余本息计算
'''


def cal_surplus_prininst(contract_amount, term_no, contract_term, rate):
    prin_inst = np.pmt(rate, contract_term, -contract_amount)
    return (contract_term + 1 - term_no) * prin_inst


'''
计算资金方罚息
'''





'''
罚息公式计算
funding_name:资金方名字
contract_amount：放款金额
contract_term:放款期数
term_no：期数
deadline：应还日期
repaid_at：客户实还日期
monthly_amount:应还金额
rate:年利率
2017年新版的罚息公式
违约服务费=借款本金*1%+逾期本金*0.5%*逾期天数
返回：
lkb_deplay_fee:量化派逾期费
'''


def cal_deplay_fee(contract_amount, deadline, repaid_at, cur_principle=0, contract_term=1, term_no=1, rate=0,
                   fund_code=0):
    contract_term = int(contract_term)
    term_no = int(term_no)
    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 = cal_surplus_prin(contract_amount, term_no, contract_term, rate, fund_code)
        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 = cal_surplus_prin(contract_amount, term_no, contract_term, rate, fund_code)
        result['lkb_deplay_fee'] = np.round(contract_amount * 0.01 + principle * 0.005 * lkb_deplay_days, 2)
        # 直接使用还款计划的应还本金
        # cur_principle = cal_cur_prin(contract_amount, term_no, contract_term, rate, fund_code)
        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):  # 2017-6-23 修改了罚息公式,当期 罚息不超过 借款本金的20%
        principle = cal_surplus_prin(contract_amount, term_no, contract_term, rate, fund_code)
        result['lkb_deplay_fee'] = np.round(contract_amount * 0.01 + principle * 0.005 * lkb_deplay_days, 2)
        # cur_principle = cal_cur_prin(contract_amount, term_no, contract_term, rate, fund_code)
        if result['lkb_deplay_fee'] > contract_amount * 0.2:
            result['lkb_deplay_fee'] = np.round(contract_amount * 0.2, 2)
        return result
    return result


def cal_fund_fee(contract_amount, deadline, repaid_at,  cur_principle, cur_interest, contract_term,
                 term_no, rate,fund_code ):
    contract_term = int(contract_term)
    term_no = int(term_no)
    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)
        result['lkb_fund_fee'] = np.round(surplus_principle * 0.01,2)
    return result


update_sql = '''
update user_repayment_plan set should_punish = %s where id = %s
'''
step = 10000
df_gen = pd.read_sql(sql=read_user_sql, con=engine_audit, chunksize=10000)
df_out = pd.DataFrame()
i = 0
for df in df_gen:
    print '-----------------------i: %s -------------------------' % i
    i += 1
    df.fillna(0, inplace=True)
    print '------------------------------>len(df.ix[df.contract_term].isnull()) : ---->', len(
        df.ix[df.contract_term].isnull())
    # 计算罚息
    df_bad = df.ix[(df.contract_term == 0)]
    print '---------------------rows with problems: len(df.ix[(df.contract_term == 0) ])------>', len(df_bad)
    if len(df_bad) > 0:
        df_bad.to_csv('df_bad_%s.csv' % i, index=None)
    df_good = df.ix[(df.contract_term != 0)]
    df_good['should_punish'] = df_good[['contract_amount', 'deadline', 'repaid_at', 'cur_principle', 'contract_term', 'term_no', 'rate', 'fund_code']].apply(lambda x: cal_deplay_fee(**dict(x))['lkb_deplay_fee'], axis=1)
    df_good['fund_punish'] = df_good[['contract_amount', 'deadline', 'repaid_at', 'cur_principle', 'cur_interest', 'contract_term', 'term_no', 'rate', 'fund_code']].apply(lambda x: cal_fund_fee(**dict(x))['lkb_fund_fee'], axis=1)
    df_good['should_punish'] = df_good['should_punish'] + df_good['fund_punish']
    # 更新数据库
    df_good['id'] = df_good['id'].map('{:.0f}'.format)
    df_good['should_punish'] = df_good['should_punish'].apply(lambda x: round(x, 2))
    up_val = df_good[['should_punish', 'id']].values.tolist()
    if update_flag == True:
        engine_audit.execute(update_sql, up_val)
    else:
        print up_val
    print '-----------------------i: %s done-------------------------' % i
print '---------------------------------done !-------------------------------'
