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

import numpy as np
import datetime
import pandas as pd
import sys
from sqlalchemy import create_engine

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


'''
目标：计算应还罚息，逾期天数=[业务还款时间- deadline]
@author linfang
1、更新 user_repayment_plan 表中的 should_punish 字段
2、罚息计算，是还款成功（即还款状态为 3）才进行计算，如果还款未完成，则不应该计算罚息。
'''

#-------------------配置数据开始-------------------------
#TODO 查询计算罚息的还款计划，如果要更新哪个放款，则指定具体的放款id或还款时间段等其他你需要的条件，但是请别删除查询项
read_user_sql = '''
SELECT id, sys_tag, fund_code, real_loan_id, ref_id, term_no, deadline, xyqb_repaid_at repaid_at
 FROM user_repayment_plan
 WHERE  DATE(xyqb_repaid_at) > DATE(deadline) and repay_status = 3 and fund_code != 160
 AND xyqb_repaid_at < '2017-01-01';
'''
#TODO 这个字段一定要注意；如果不进行更新表中的数据，请设置为 False ！！！！！！
#TODO False 表示输出到控制台
update_flag=True
#TODO 数据源
engine_audit = create_engine('mysql+mysqldb://yulong_rw:TouBStYwN8wkdxVt@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:
        return np.ppmt(rate, i, contract_term, -contract_amount)



'''
罚息公式计算
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(product_id, sys_tag, contract_amount, deadline, repaid_at, contract_term=1, term_no=1,
                   monthly_amount=0, 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 = np.round(cal_cur_prin(contract_amount, term_no, contract_term, rate, fund_code), 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):# 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)
        if result['lkb_deplay_fee'] > contract_amount * 0.2:
           result['lkb_deplay_fee'] = np.round(contract_amount * 0.2, 2)
        return result
    # lkb_deplay_days = (repaid_at - deadline).days
    if product_id == 1:
        # 如何之后跑数据的话，需要去掉repaid_at <= datetime.datetime(2015,11,16,20,0,0) 这个条件
        if sys_tag == 'LKB':
            lkb_deplay_days = lkb_deplay_days - 1
        fee = lkb_deplay_days * 10
        if fee > 150:
            fee = 150
        result['lkb_deplay_fee'] = fee
    elif product_id == 2:
        if sys_tag == 'LKB':
            lkb_deplay_days = lkb_deplay_days - 1
        result['lkb_deplay_fee'] = contract_amount * lkb_deplay_days * 0.005
    elif product_id == 5:
        result['lkb_deplay_fee'] = np.round(contract_amount * 0.01 + monthly_amount * 0.005 * lkb_deplay_days, 2)
    elif product_id in (7, 9, 10, 11, 12, 13, 14,15, 16, 17, 19, 20, 21, 22, 23, 25, 26, 27, 28, 29, 32, 34, 35):
        result['lkb_deplay_fee'] = np.round(contract_amount * 0.01 + contract_amount * (
            contract_term + 1 - term_no) / contract_term * 0.005 * lkb_deplay_days, 2)
    elif product_id == 3:
        # 老马上
        if sys_tag == 'LKB':
            lkb_deplay_days = lkb_deplay_days - 1
            result['lkb_deplay_fee'] = np.round(contract_amount * 0.005 * lkb_deplay_days, 2)
        else:
            if repaid_at < datetime.datetime(2015, 11, 2, 21, 0, 0):
                result['lkb_deplay_fee'] = np.round(monthly_amount * 0.005 * lkb_deplay_days, 2)
            elif repaid_at >= datetime.datetime(2015, 11, 2, 21, 0, 0) and repaid_at < datetime.datetime(2015, 11, 3, 0,0, 0):
                result['lkb_deplay_fee'] = np.round(contract_amount * 0.01 * lkb_deplay_days, 2)
            elif repaid_at >= datetime.datetime(2015, 11, 3, 0, 0, 0) and repaid_at < datetime.datetime(2015, 11, 3, 17,0, 0, 0):
                result['lkb_deplay_fee'] = np.round(contract_amount * 0.005 * lkb_deplay_days, 2)
            elif repaid_at >= datetime.datetime(2015, 11, 3, 17, 0, 0, 0):
                result['lkb_deplay_fee'] = np.round(contract_amount * 0.01 + contract_amount * (
                    contract_term + 1 - term_no) / contract_term * 0.005 * lkb_deplay_days, 2)
    elif product_id == 18:
        # 新马上
        pass
    else:
        print 'product_id error !!!', product_id,sys_tag,contract_amount,contract_term,deadline,repaid_at
    return result





read_loan_sql = '''
SELECT ref_id, contract_loan_amount contract_amount,contract_term, monthly_interest_rate rate, monthly_required_repayment_amount monthly_amount
 from audit.loan_manifest WHERE is_active in (1,-2) and ref_id IN %s
'''

read_xyqb_sql = '''
SELECT loan_application_history_id loan_id, product_id FROM loan_application_manifest_history WHERE loan_application_history_id IN %s
'''

read_old_sql = '''
select id loan_id,productId product_id from t_xyqb_transaction where id in %s
'''

update_sql = '''
update user_repayment_plan set should_punish = %s where id = %s
'''


engine_xyqb = create_engine('mysql+mysqldb://bowang:in78u6ytgbhj56r@192.168.4.6:8066/xyqb?charset=utf8',
                            echo=True)
engine_lkbpayment = create_engine('mysql+mysqldb://lkbpayment_w:ySsTfEzCPIh2de4wbe5BqdEGes8YGPh9@172.16.90.7:3306/lkbpayment?charset=utf8', echo=True)

cols=['id','product_id', 'sys_tag',  'contract_amount', 'deadline', 'repaid_at', 'contract_term', 'term_no',
         'monthly_amount', 'rate', 'fund_code']

step = 5000

df_gen = pd.read_sql(sql=read_user_sql, con=engine_audit, chunksize=step)
i = 0
for df in df_gen:
    print '-----------------------i: %s -------------------------' % i
    i += 1
    df.fillna(0, inplace=True)
    ref_ids = tuple(df['ref_id'].astype(int))
    df_loan = pd.read_sql(read_loan_sql % str(ref_ids).replace(',)',')'), con=engine_audit)
    df['ref_id'] = df['ref_id'].astype(int)
    df_loan['ref_id'] = df_loan['ref_id'].astype(int)
    df = pd.merge(df, df_loan, on='ref_id', how='left')
    print '------------------------------>len(df.ix[df.contract_term].isnull()) : ---->', len(df.ix[df.contract_term].isnull())
    #提取自主放贷
    df.real_loan_id=df.real_loan_id.astype(str)
    df_zzfd=df.ix[df.real_loan_id.str.contains('z')]
    df_zzfd['product_id'] = 7
    #非自主放贷
    df_other=df.ix[~df.real_loan_id.isin(df_zzfd.real_loan_id)]
    df_other['real_loan_id'] = df_other['real_loan_id'].astype(str)
    #新老系统
    df_lkb = df_other.ix[df_other.real_loan_id.str.contains('o')]
    df_lkb['real_loan_id']=df_lkb['real_loan_id'].apply(lambda x:str(x).replace('o',''))
    df_xyqb = df_other.ix[~df_other.real_loan_id.str.contains('o')]
    #非自主放贷---product_id 获取
    xyqb_loan_ids = tuple(df_xyqb.real_loan_id.tolist())
    old_loan_ids = tuple(df_lkb.real_loan_id.tolist())
    if len(xyqb_loan_ids) > 0:
       df_product = pd.read_sql(read_xyqb_sql % str(xyqb_loan_ids).replace(',)',')'), con=engine_xyqb)
       df_product.loan_id=df_product.loan_id.astype(str)
       df_xyqb = pd.merge(df_xyqb,df_product,left_on=['real_loan_id'],right_on=['loan_id'],how='left')
    if len(old_loan_ids) > 0:
       df_product = pd.read_sql(read_old_sql % str(old_loan_ids).replace(',)',')'), con=engine_lkbpayment)
       df_product.loan_id = df_product.loan_id.astype(str)
       df_lkb = pd.merge(df_lkb,df_product,left_on='real_loan_id',right_on='loan_id',how='left')

    #合并所有的放款数据
    if len(df_xyqb) ==0:
        df_xyqb['product_id'] =0
    if len(df_lkb) == 0 :
        df_lkb['product_id'] = 0
    df = pd.concat((df_zzfd[cols],df_xyqb[cols],df_lkb[cols]),axis=0)
    df.fillna(0,inplace=True)
    df_null_product = df.ix[df.product_id.isnull()]
    print '------------------------------>len(df.ix[df.product_id.isnull()]) : ---->', len(df_null_product)
    if len(df_null_product) > 0:
        df_null_product.to_csv('df_null_product_%s.csv' % i, index=None)
    # 计算罚息
    df_bad = df.ix[(df.contract_term == 0) | (df.product_id == 0)]
    print '---------------------rows with problems: len(df.ix[(df.contract_term == 0) or (df.product_id == 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.product_id != 0)]
    df_good['should_punish'] = df_good[
        ['product_id', 'sys_tag', 'contract_amount', 'deadline', 'repaid_at', 'contract_term', 'term_no',
         'monthly_amount', 'rate', 'fund_code']].apply(lambda x: cal_deplay_fee(**dict(x))['lkb_deplay_fee'], axis=1)
    # 更新数据库
    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 !-------------------------------'
