import datetime
from decimal import Decimal
from math import ceil
import uuid
from brewman.models import DBSession
from brewman.models.master import LedgerBase, Employee
from brewman.models.operations import journals_valid
from brewman.models.validation_exception import ValidationError
from brewman.models.voucher import Journal, Voucher, VoucherType, SalaryDeduction
from brewman.views.services.session import get_last_day

__author__ = 'tanshu'

def salary_deduction_create_voucher(json, user):
    dt = get_last_day(datetime.datetime.strptime(json['Date'], '%d-%b-%Y'))
    days_in_month = dt.day
    voucher = Voucher(date=dt, user_id=user.id, type=VoucherType.by_id(12))
    DBSession.add(voucher)
    exp, total = 0, 0
    for item in json['SalaryDeductions']:
        item_exp, item_total = add_salary_deduction(item, days_in_month, voucher)
        exp += item_exp
        total += item_total
    ledger = LedgerBase.by_id(LedgerBase.esi_pf_expense())
    journal = Journal(amount=exp, debit=1, ledger_id=ledger.id, cost_center_id=ledger.costcenter_id)
    DBSession.add(journal)
    voucher.journals.append(journal)
    ledger = LedgerBase.by_id(LedgerBase.esi_pf_payable())
    journal = Journal(amount=total, debit=-1, ledger_id=ledger.id, cost_center_id=ledger.costcenter_id)
    DBSession.add(journal)
    voucher.journals.append(journal)

    journals_valid(voucher)
    return voucher


def salary_deduction_update_voucher(voucher, json, user):
    dt = get_last_day(datetime.datetime.strptime(json['Date'], '%d-%b-%Y'))
    if dt != voucher.date.date():
        raise ValidationError("Date Cannot be changed for Salary Deduction voucher!")
    days_in_month = voucher.date.day
    voucher.user_id = user.id
    voucher.posted = False
    voucher.last_edit_date = datetime.datetime.utcnow()

    newDeductions = json['SalaryDeductions']
    exp, total, journals = 0, 0, []
    for i in range(len(voucher.salary_deductions), 0, -1):
        item = voucher.salary_deductions[i - 1]
        found = False
        for i in range(len(newDeductions), 0, -1):
            j = newDeductions[i - 1]
            if 'SalaryDeductionID' in j and item.id == uuid.UUID(j['SalaryDeductionID']):
                journals.append(item.journal.id)
                exp += item.esi_er + item.pf_er
                total += item.esi_ee + item.pf_ee + item.esi_er + item.pf_er
                newDeductions.remove(j)
                break
        if not found:
            voucher.salary_deductions.remove(item)
            voucher.journals.remove(item.journal)
    for j in newDeductions:
        item_exp, item_total = add_salary_deduction(j, days_in_month, voucher)
        exp += item_exp
        total += item_total

    journal = [i for i in voucher.journals if i.ledger_id == LedgerBase.esi_pf_expense()]
    journal = journal[0]
    journal.amount = exp
    journal = [i for i in voucher.journals if i.ledger_id == LedgerBase.esi_pf_payable()]
    journal = journal[0]
    journal.amount = total

    journals_valid(voucher)
    return voucher


def add_salary_deduction(item, days_in_month, voucher):
    ledger = Employee.by_id(uuid.UUID(item['Journal']['Ledger']['LedgerID']))
    gross_salary = int(item['GrossSalary'])
    days_worked = int(item['DaysWorked'])
    esi_ee, esi_er, esi_both = esi_contribution(gross_salary, days_worked, days_in_month)
    pf_ee, pf_er, pf_both = pf_contribution(gross_salary, days_worked, days_in_month)
    journal = Journal(amount=esi_ee + pf_ee, debit=1, ledger_id=ledger.id, cost_center_id=ledger.costcenter_id)
    sd = SalaryDeduction(journal=journal, gross_salary=gross_salary, days_worked=days_worked, esi_ee=esi_ee,
        pf_ee=pf_ee, esi_er=esi_er, pf_er=pf_er)
    voucher.journals.append(journal)
    voucher.salary_deductions.append(sd)
    DBSession.add(journal)
    DBSession.add(sd)
    return esi_er + pf_er, esi_both + pf_both


def esi_contribution(gross_salary, days_worked, days_in_month):
    limit = 15000
    employee_rate = .0175
    employer_rate = .0475
    employee = 0 if gross_salary > limit else ceil(employee_rate * gross_salary * days_worked / days_in_month)
    employer = 0 if gross_salary > limit else ceil(employer_rate * gross_salary * days_worked / days_in_month)
    return employee, employer, employee + employer


def pf_contribution(gross_salary, days_worked, days_in_month):
    limit = 6500
    employee_rate = .12
    employer_rate = .12 + .011 + .005 + .0001
    employee = 0 if gross_salary > limit else ceil(employee_rate * gross_salary * days_worked / days_in_month)
    employer = 0 if gross_salary > limit else ceil(employer_rate * gross_salary * days_worked / days_in_month)
    return employee, employer, employee + employer
