# coding: utf-8


import datetime
import multiprocessing as mtp
from itertools import product

import numpy as np
import pandas as pd
from dateutil.relativedelta import relativedelta
from sqlalchemy import create_engine

pd.options.mode.chained_assignment = None

engine_audit = create_engine('mysql+mysqldb://yulong:ohVU1nPITynSZwR2@172.16.3.201:3306/test_shiyao?charset=utf8',
                             echo=True)


def cal_prin_inst(x):
    fund_id = int(x['funding_code'])
    a = x['contract_loan_amount']
    c = int(x['contract_term'])
    b = x['monthly_interest_rate'] * 12
    term = int(x['term_no'])
    result = {}
    result['funding_service_fee'] = 0.0
    result['ref_id'] = int(x['ref_id'])
    result['term_no'] = term
    if fund_id in (170, 190, 260):
        result['required_repayment'] = np.pmt(b / 12, c, -a)
        result['principle'] = np.ppmt(b / 12, term, c, -a)
        result['interest'] = np.ipmt(b / 12, term, c, -a)
    elif fund_id == 140:
        result['principle'] = np.ceil(np.ppmt(b / 12, term, c, -a) * 100) / 100
        result['interest'] = np.ceil(np.ipmt(b / 12, term, c, -a) * 100) / 100
        result['required_repayment'] = result['principle'] + result['interest']
    elif fund_id == 180:
        result['required_repayment'] = np.round(np.pmt(b / 12, c, -a), 2)
        if term == 1:
            money = a
            term_days = (x['deadline'] - (x['deadline'] + relativedelta(months=-1))).days
            result['interest'] = np.round(b / 360 * money * term_days, 2)
            result['principle'] = result['required_repayment'] - result['interest']
        # 合同金额 - 第一期本金
        # 第二期还款日期 - 第一期还款日期
        else:
            money = a
            for i in xrange(1, term):
                term_days = (
                    (x['deadline'] + relativedelta(months=-(term - i))) - (
                        x['deadline'] + relativedelta(months=-(term - i + 1)))).days
                money -= result['required_repayment'] - np.round(b / 360 * money * term_days, 2)

            term_days = (x['deadline'] - (x['deadline'] + relativedelta(months=-1))).days
            result['interest'] = np.round(b / 360 * money * term_days, 2)
            result['principle'] = (term == c) and money or result['required_repayment'] - result['interest']
            result['required_repayment'] = (term == c) and (result['interest'] + result['principle']) or result[
                'required_repayment']

    elif fund_id in (290, 340):
        m = a
        for j in xrange(1, term):
            m -= np.round(np.ppmt(b / 12, j, c, -a), 2)
        result['interest'] = np.round(m * b / 12, 2)
        if term == c:
            result['principle'] = a
            for i in xrange(1, c):
                result['principle'] -= np.round(np.ppmt(b / 12, i, c, -a), 2)
            result['required_repayment'] = result['interest'] + result['principle']
        else:
            result['required_repayment'] = np.round(np.pmt(b / 12, c, -a), 2)
            result['principle'] = result['required_repayment'] - result['interest']
    elif fund_id == 150:
        result['principle'] = np.round(np.ppmt(b / 12, term, c, -a), 2)
        result['required_repayment'] = np.round(np.pmt(b / 12, c, -a), 2)
        if term == c:
            result['principle'] = a
            for i in xrange(1, c):
                result['principle'] -= np.round(np.ppmt(b / 12, i, c, -a), 2)
        result['interest'] = result['required_repayment'] - result['principle']
    elif fund_id == 200:
        result['required_repayment'] = np.round(np.pmt(b / 12, c, -a), 2)
        result['interest'] = np.round(np.ipmt(b / 12, term, c, -a), 2)
        if term == c:
            result['principle'] = a
            for i in xrange(1, c):
                result['principle'] -= np.round(np.ppmt(b / 12, i, c, -a), 2)
        else:
            result['principle'] = np.round(np.ppmt(b / 12, term, c, -a), 2)
    elif fund_id in (210, 240, 330, 350, 363, 365, 415, 380):
        result['interest'] = np.round(np.ipmt(b / 12, term, c, -a), 2)
        if term == c:
            result['principle'] = a
            for i in xrange(1, c):
                result['principle'] -= (np.round(np.pmt(b / 12, c, -a), 2) - np.round(np.ipmt(b / 12, i, c, -a), 2))
            result['required_repayment'] = result['interest'] + result['principle']
        else:
            result['required_repayment'] = np.round(np.pmt(b / 12, c, -a), 2)
            result['principle'] = result['required_repayment'] - result['interest']
    elif fund_id == 250:
        result['required_repayment'] = np.round(np.pmt(b / 12, c, -a), 2)
        result['interest'] = np.round(np.ipmt(b / 12, term, c, -a), 2)
        result['principle'] = result['required_repayment'] - result['interest']
    elif fund_id == 300:
        result['interest'] = 0
        if term == c:
            result['principle'] = a
            for i in xrange(1, c):
                result['principle'] -= np.round(a / c, 2)
            result['required_repayment'] = result['principle'] + result['interest']
        else:
            result['principle'] = np.round(a / c, 2)
            result['required_repayment'] = result['principle'] + result['interest']
    elif fund_id == 420:
        result['required_repayment'] = np.round(np.pmt(b / 12, c, -a), 2)
        if term == c:
            result['principle'] = a
            for i in xrange(1, c):
                result['principle'] -= np.round(np.ppmt(b / 12, term, c, -a), 2)
        else:
            result['principle'] = np.round(np.ppmt(b / 12, term, c, -a), 2)
        result['interest'] = result['required_repayment'] - result['principle']
    elif fund_id == 360:
        rate = 0.08
        result['required_repayment'] = np.round(np.pmt(b / 12, c, -a), 2)
        result['interest'] = np.round(np.ipmt(rate / 12, term, c, -a), 2)
        if term == c:
            result['principle'] = a
            for i in xrange(1, c):
                result['principle'] -= (
                    np.round(np.pmt(rate / 12, c, -a), 2) - np.round(np.ipmt(rate / 12, i, c, -a), 2))
            result['required_repayment'] = result['interest'] + result['principle']
        else:
            result['principle'] = np.round(np.ppmt(rate / 12, term, c, -a), 2)
        result['funding_service_fee'] = result['required_repayment'] - result['principle'] - result['interest']
    elif fund_id == 230:
        rate = 0.075
        result['required_repayment'] = np.round(np.pmt(b / 12, c, -a), 2)
        result['interest'] = np.round(np.ipmt(rate / 12, term, c, -a), 2)
        if term == c:
            result['principle'] = a
            for i in xrange(1, c):
                result['principle'] -= (
                    np.round(np.pmt(rate / 12, c, -a), 2) - np.round(np.ipmt(rate / 12, i, c, -a), 2))
            result['required_repayment'] = result['interest'] + result['principle']
        else:
            result['principle'] = np.round(np.ppmt(rate / 12, term, c, -a), 2)
        result['funding_service_fee'] = result['required_repayment'] - result['principle'] - result['interest']
    elif fund_id in (270, 310, 320, 390, 410):
        result['interest'] = np.round(np.ipmt(b / 12, term, c, -a), 2)
        if term == c:
            result['principle'] = a
            for i in xrange(1, c):
                result['principle'] -= np.round(np.ppmt(b / 12, i, c, -a), 2)
            result['required_repayment'] = result['interest'] + result['principle']
        else:
            result['required_repayment'] = np.round(np.pmt(b / 12, c, -a), 2)
            result['principle'] = result['required_repayment'] - result['interest']
    return result


def create_deadline(x):
    result = {}
    loan_time = x['loan_paid_at']
    d = loan_time.day
    y = loan_time.year
    m = loan_time.month
    term = int(x['term_no'])
    funding_code = int(x['funding_code'])
    deadline = datetime.date(y, m, d) + relativedelta(months=+term)
    if funding_code == 240:
        deadline = deadline + relativedelta(days=-1)
        d1 = deadline.day
        y1 = deadline.year
        m1 = deadline.month
        if d1 > 28 and d1 <= 31:
            deadline = datetime.date(y1, m1, 28)
    result['deadline'] = deadline
    return result


query_loan_info = """
SELECT 
loan_id,
funding_code,
contract_term,
ref_id,loan_paid_at,
contract_loan_amount,
monthly_interest_rate 
FROM audit.loan_manifest 
WHERE is_active in(1,-2)
AND loan_paid_at>='%s'
AND loan_paid_at<'%s'
GROUP BY 1
HAVING COUNT(loan_id)=1
"""

query_loan_info_temp = """
SELECT 
loan_id,
funding_code,
contract_term,
ref_id,loan_paid_at,
contract_loan_amount,
monthly_interest_rate 
FROM audit.loan_manifest 
WHERE ref_id in(
select DISTINCT ref_id from audit.funding_repayment_record
)
"""

query_loan_info_t = """
SELECT 
loan_id,
funding_code,
contract_term,
ref_id,loan_paid_at,
contract_loan_amount,
monthly_interest_rate 
FROM audit.loan_manifest 
WHERE is_active in(1,-2)
AND loan_paid_at>='2016-01-01'
AND loan_paid_at<'2017-09-01'
and ref_id not in (
select DISTINCT ref_id from test_shiyao.funding_repayment_plan
)
GROUP BY 1
HAVING COUNT(loan_id)=1
"""


def create_plan(y, m):
    loan_at = datetime.date(y, m, 1)
    loan_at_end = datetime.date(y, m, 1) + relativedelta(months=+1)
    df = pd.read_sql(query_loan_info % (loan_at, loan_at_end), con=engine_audit)
    # df = pd.read_sql(query_loan_info_t, con=engine_audit)
    df_no = pd.read_excel('contract_term_no.xlsx')
    df_no.contract_term = df_no.contract_term.astype(int)
    df_no.term_no = df_no.term_no.astype(int)
    df.contract_term = df.contract_term.astype(int)
    df = pd.merge(df, df_no, on='contract_term')
    df['deadline'] = df.apply(lambda x: create_deadline(x)['deadline'], axis=1)
    df['principle'] = 0.0
    df['interest'] = 0.0
    df['required_repayment'] = 0.0
    df['funding_service_fee'] = 0.0
    df_t = pd.DataFrame(df.apply(lambda x: cal_prin_inst(x), axis=1),
                        columns=['ref_id', 'term_no', 'principle', 'interest', 'funding_service_fee',
                                 'required_repayment'])
    df.drop(['principle', 'interest', 'funding_service_fee', 'required_repayment'], axis=1, inplace=True)
    df['ref_id'] = df['ref_id'].astype(int)
    df['term_no'] = df['term_no'].astype(int)
    df_t['ref_id'] = df_t['ref_id'].astype(int)
    df_t['term_no'] = df_t['term_no'].astype(int)
    df = pd.merge(df, df_t, on=['ref_id', 'term_no'])
    df_out = df[
        ['loan_id', 'funding_code', 'term_no', 'ref_id', 'deadline', 'principle', 'interest', 'funding_service_fee',
         'required_repayment']]
    df_out['principle'] = df_out['principle'].apply(lambda x: np.round(x, 2))
    df_out['interest'] = df_out['interest'].apply(lambda x: np.round(x, 2))
    df_out['funding_service_fee'] = df_out['funding_service_fee'].apply(lambda x: np.round(x, 2))
    df_out['required_repayment'] = df_out['required_repayment'].apply(lambda x: np.round(x, 2))
    df_out.to_sql('funding_repayment_plan', con=engine_audit, chunksize=10000, if_exists='append', index=None)


create_plan(2017, 9)

# years = (2015, 2016, 2017)
# months = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
#
# if __name__ == '__main__':
#     pool = mtp.Pool(processes=4)
#     for y, m in product(years, months):
#         print y, m
#         if y == 2017 and m > 7:
#             continue
#         pool.apply_async(create_plan, (y, m))
#     pool.close()
#     pool.join()
