import datetime
from decimal import Decimal
import uuid

from sqlalchemy import or_, func

from brewman.models import DBSession
from brewman.models.master import Employee, AttendanceType, Ledger
from brewman.models.operations import journals_valid
from brewman.models.voucher import Journal, Voucher, VoucherType, Attendance, ServiceCharge
from brewman.views.services.session import get_first_day


__author__ = 'tanshu'


def service_charge_create_voucher(json, user):
    date = datetime.datetime.strptime(json['Date'], '%d-%b-%Y')

    voucher = Voucher(date=date, user_id=user.id, type=VoucherType.by_id(13))
    DBSession.add(voucher)

    employees = get_employees(get_first_day(date), date + datetime.timedelta(1), json['ServiceCharges'])
    amount = balance(date) * Decimal(.7) * Decimal(.9)

    total_points = 0
    for item in employees:
        total_points += item['Points'] * item['DaysWorked']
    point_value = round(amount / total_points, 2)
    total_amount = 0

    for item in employees:
        item_amount = round(item['Points'] * item['DaysWorked'] * point_value)
        employee = item['Employee']
        journal = Journal(amount=item_amount, debit=-1, ledger_id=employee.id, cost_centre_id=employee.cost_centre_id)
        sc = ServiceCharge(journal=journal, days_worked=item['DaysWorked'], points=item['Points'])
        voucher.journals.append(journal)
        voucher.service_charges.append(sc)
        DBSession.add(journal)
        DBSession.add(sc)
        total_amount += item_amount

    sc = Ledger.by_id(Ledger.service_charge_id())
    journal = Journal(amount=total_amount, debit=1, ledger_id=sc.id, cost_centre_id=sc.cost_centre_id)
    voucher.journals.append(journal)
    DBSession.add(journal)
    journals_valid(voucher)
    return voucher


def service_charge_update_voucher(voucher, json, user):
    date = datetime.datetime.strptime(json['Date'], '%d-%b-%Y')
    voucher.date = date
    voucher.user_id = user.id
    voucher.posted = False
    voucher.last_edit_date = datetime.datetime.utcnow()

    employees = get_employees(get_first_day(date), date + datetime.timedelta(1), json['ServiceCharges'], voucher.journals)
    amount = balance(date, voucher.id) * Decimal(.7) * Decimal(.9)

    total_points = 0
    for item in employees:
        total_points += item['Points'] * item['DaysWorked']
    point_value = round(amount / total_points, 2)
    total_amount = 0

    for item in voucher.service_charges:
        employee = [e for e in employees if e['Employee'].id == item.journal.ledger_id][0]
        item_amount = round(employee['Points'] * employee['DaysWorked'] * point_value)
        item.days_worked = employee['DaysWorked']
        item.points = employee['Points']
        item.journal.amount = item_amount
        total_amount += item_amount

    journal = [j for j in voucher.journals if j.ledger_id == Ledger.service_charge_id()][0]
    journal.amount = total_amount

    journals_valid(voucher)
    return voucher


def get_employees(start_date, finish_date, json, voucher_employees=None):
    details = []
    employees = DBSession().query(Employee) \
        .filter(Employee.joining_date <= finish_date) \
        .filter(or_(Employee.is_active, Employee.leaving_date >= start_date)) \
        .order_by(Employee.cost_centre_id).order_by(Employee.designation).order_by(Employee.name).all()

    check_if_employees_changed(json, employees, voucher_employees)

    for employee in employees:
        att = DBSession.query(Attendance) \
            .filter(Attendance.employee_id == employee.id) \
            .filter(Attendance.date >= start_date) \
            .filter(Attendance.date < finish_date) \
            .filter(Attendance.is_valid == True) \
            .all()
        att = sum(map(lambda x: AttendanceType.by_id(x.attendance_type).value, att))
        details.append({'Employee': employee, 'DaysWorked': round(Decimal(att), 2)})
    for item in details:
        j = [x for x in json if uuid.UUID(x['EmployeeID']) == item['Employee'].id][0]
        item['Points'] = round(Decimal(j['Points']), 2)
        json.remove(j)
    return details


def balance(date, voucher_id=None):
    amount = DBSession.query(func.sum(Journal.amount * Journal.debit)) \
        .join(Journal.voucher) \
        .filter(Voucher.date < date + datetime.timedelta(1)) \
        .filter(Voucher.type != VoucherType.by_name('Issue').id) \
        .filter(Journal.ledger_id == Ledger.service_charge_id())
    if voucher_id is not None:
        amount = amount.filter(Voucher.id != voucher_id)
    amount = amount.scalar()
    return 0 if amount is None else amount * -1


def check_if_employees_changed(json, db, voucher):
    json = set([uuid.UUID(x['EmployeeID']) for x in json])
    db = set([x.id for x in db])
    voucher = set([x.ledger_id for x in voucher]) if voucher is not None else None
    if voucher is None:
        if len(json) != len(db):
            raise ValueError("Employee missing in json data")
        if len(json ^ db) != 0:
            raise ValueError("Employee missing in json data")
    else:
        if len(json) != len(db) != len(voucher):
            raise ValueError("Employee missing in json data")
        if len(json ^ db) != 0 or len(db ^ voucher) != 0:
            raise ValueError("Employee missing in json data")