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

# from api.db_connect import *
import numpy as np
import datetime
import pandas as pd
from decimal import Decimal
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.ref_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.id in (4775015,13699135,4864585,10403491,14023021,10832639,12008815,10853291,4301879,13287685,11573917,4779607,13742293,10365019,11553475,14580895,12916795,13911511,8994139,11487055,11817439,15644719,4247083,10332679,15760377,11571775,13895353,11606641,15754995,11561665,15606439,13014299,13807459,10282111,12515395,4780291,13902949,4761541,10559023,14415019,10959971,10745407,9378703,14444797,15615711,11220653,13920001,13545217,4683485,10769671,14287729,15828507,9069751,14755609,16963339,10735015,11533387,15840135,12034657,15721821,14555149,14144305,10868297,13308733,14156563,15736779,15742785,14163055,10768735,15735681,10857341,12266897,13606735,11571493,15742479,6206467,14147245,9038713,13380133,15737133,16187587,9077323,17104759,16156489,4843837,16164091,13381843,13379551,13383283,16211215,9112873,13383301,13382461,11043875,8973289,14035141,11016569,10369021,10984583,10655257,12855109,15657103,6206851,11364493,15080977,15628705,13634233,11060357,16474093,14008321,10712905,15617629,10511893,13267477,13839097,13588795,13311103,15749865,15727101,15747999,13909609,14003449,10755181,13932175,13585117,15739185,10731211,13038637,15655039,15760299,15101515,10637431,17040421,9358441,16983085,9125593,15736347,18349033,11246465,15835545,12989767,15613143,16891159,10730821,11557201,15835221,18337705,11456947,18332905,18330301,11657689,15560943,15840597,9078805,11428417,18336817,18319051,18329899,16242301,18330175,18328963,11230439,9080923,11510455,11507503,18327277,13961989,15339693,15338487,15333801,14600089,14599243,14585713,14519131,11649391,14583841,11740909,11759611,14581057,14581435,11852725,11749825,11792365,14477329,11829133,17987269,9207991,11765539,9179101,11742745,15844209,15942117,15863913,15864495,15868065,15923661,14478625,15919335,14528929,11727331,15869787,15866655,11835961,11788261,14528851,15917187,15869685,14526817,14464321,15870609,15862899,14484025,15841887,14583295,11813413,12046555,14344813,14339473,11942425,11953711,12013771,12059689,14163529,14425939,14401057,14394619,14400925,14395297,14381095,11858641,14448967,14392471,14388925,14434507,14451847,11994727,14462455,10247353,14165653,12096001,14436001,14452903,14435929,14300245,11989483,14375929,11941081,14395837,14402641,14394949,12028513,12092737,12077305,14387665,14374897,14254213,14380267,14374801,12056881,12016279,16039467,15991737,16065501,14318995,12022261,16056645,14318845,16044039,14312893,10091581,16051917,16038549,14319841,11924791,16040697,16046283,14445013,11902843,14390497,14163811,16030701,14449345,15970761,11880889,14127847,15785175,14135347,14120623,14123209,15796251,15784791,15792123,12153235,12099055,12112669,14207431,14190289,14184163,18368707,18382999,18369991,18379243,18364357,18320899,18368389,18370969,18367063,18370237,18373267,18382483)
'''
#  t1.xyqb_repaid_at >= '2017-10-01 00:00:00' and t1.xyqb_repaid_at < '2017-11-10 00:00:00'
# and t1.repayment_plan_id in ("13594052")


sql_query_surplus_principle = """
SELECT SUM(principle) surplus_prin FROM new_transaction.user_repayment_plan WHERE ref_id=%s AND term_no>=%s
"""

# 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,ref_id=0):
    prin = 0
    if(ref_id!=0):
        df_surplus_prin = pd.read_sql(sql=sql_query_surplus_principle % (ref_id, term_no) ,con=engine_audit)
        if len(df_surplus_prin) > 0:
            prin = df_surplus_prin['surplus_prin'].tolist().pop()
        return prin
    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, ref_id=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,ref_id)
        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,ref_id)
        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,ref_id)
        result['lkb_deplay_fee'] = np.round(contract_amount * 0.01 + np.round(principle,2) * 0.005 * lkb_deplay_days, 2)
        # result['lkb_deplay_fee'] = '{:.2f}'.format(Decimal(str(np.round(contract_amount * 0.01 + principle * 0.005 * lkb_deplay_days, 10))))
        # 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,ref_id=0 ):
    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,ref_id)
        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.ref_id = df_good.ref_id.astype(int)
    df_good['should_punish'] = df_good[['contract_amount', 'deadline', 'repaid_at', 'cur_principle', 'contract_term', 'term_no', 'rate', 'fund_code','ref_id']].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','ref_id']].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 !-------------------------------'
