import datetime
from decimal import Decimal
import uuid
from pyramid.security import authenticated_userid
from pyramid.view import view_config
from sqlalchemy import func, and_
from sqlalchemy.orm import aliased
import transaction
from brewman.models import DBSession
from brewman.models.master import LedgerBase, CostCenter, Product, LedgerType
from brewman.models.validation_exception import TryCatchFunction
from brewman.models.voucher import Journal, Voucher, VoucherType, Batch, Inventory, SalaryDeduction, Fingerprint, Attendance

__author__ = 'tanshu'


@view_config(request_method='POST', route_name='api_rebase', renderer='json', permission='Rebase')
@TryCatchFunction
def rebase(request):
    date = request.matchdict.get('date', None)
    date = datetime.datetime.strptime(date, '%d-%b-%Y')
    user_id = uuid.UUID(authenticated_userid(request))
    voucher_l = opening_ledgers(date, user_id)
    voucher_b, batches = opening_batches(date, user_id)
    delete_data(date, batches)
    DBSession.add(voucher_l)
    for j in voucher_l.journals:
        DBSession.add(j)
    DBSession.add(voucher_b)
    for j in voucher_b.journals:
        DBSession.add(j)
    for i in voucher_b.inventories:
        DBSession.add(i)
    transaction.commit()
    return {}


def opening_ledgers(date, user_id):
    running_total = 0

    sum_func = func.sum(Journal.signed_amount)
    query = DBSession.query(LedgerBase, sum_func) \
        .join(Journal, Voucher.journals).join(LedgerBase, Journal.ledger) \
        .filter(Voucher.date < date).filter(Voucher.type != VoucherType.by_name('Issue').id) \
        .having(sum_func != 0) \
        .group_by(LedgerBase).all()

    dt = date - datetime.timedelta(days=1)
    voucher = Voucher(date=dt, narration='Opening Ledgers', user_id=user_id, posted=True,
                      type=VoucherType.by_name('Opening Ledgers'))
    for ledger, amount in query:
        amount = round(Decimal(amount), 2)
        if not ledger.type_object().balance_sheet:
            pass
        if amount != 0:
            running_total += amount
            journal = Journal(amount=abs(amount), debit=-1 if amount < 0 else 1, ledger_id=ledger.id,
                              cost_center_id=ledger.costcenter_id)
            voucher.journals.append(journal)
    if running_total != 0:
        journal = Journal(amount=abs(amount), debit=-1 if amount * -1 < 0 else 1, ledger_id=LedgerBase.suspense(),
                          cost_center_id=CostCenter.cost_center_overall())
        voucher.journals.append(journal)
    return voucher


def opening_batches(date, user_id):
    total = 0
    batches = []
    sum_func = func.sum(Journal.debit * Inventory.quantity)
    query = DBSession.query(Batch, sum_func) \
        .join(Journal, Voucher.journals).join(Inventory, Voucher.inventories).join(Batch, Inventory.batch) \
        .filter(Voucher.date < date) \
        .filter(Journal.cost_center_id == CostCenter.cost_center_purchase()) \
        .having(sum_func != 0) \
        .group_by(Batch).all()

    dt = date - datetime.timedelta(days=1)
    voucher = Voucher(date=dt, narration='Opening Batches', user_id=user_id, posted=True,
                      type=VoucherType.by_name('Opening Batches'))
    for batch, quantity in query:
        quantity = round(Decimal(quantity), 2)
        if quantity != 0:
            total += quantity * batch.rate * (1 + batch.tax) * (1 - batch.discount)
            inventory = Inventory(product_id=batch.product_id, batch=batch, quantity=quantity, rate=batch.rate,
                                  tax=batch.tax, discount=batch.discount)
            voucher.inventories.append(inventory)
            batches.append(batch.id)
    voucher.journals.append(Journal(amount=abs(total), debit=-1, ledger_id=LedgerBase.all_purchases(),
                                    cost_center_id=CostCenter.cost_center_overall()))
    voucher.journals.append(Journal(amount=abs(total), debit=1, ledger_id=LedgerBase.all_purchases(),
                                    cost_center_id=CostCenter.cost_center_purchase()))
    return voucher, batches


def delete_data(date, batches):
    sub_voucher = aliased(Voucher)
    sub_query = DBSession.query(sub_voucher.id).filter(sub_voucher.date < date).subquery()

    Inventory.__table__.delete(Inventory.voucher_id.in_(sub_query)).execute()
    SalaryDeduction.__table__.delete(SalaryDeduction.voucher_id.in_(sub_query)).execute()
    Journal.__table__.delete(Journal.voucher_id.in_(sub_query)).execute()
    Voucher.__table__.delete(Voucher.date < date).execute()

    Batch.__table__.delete(and_(~Batch.inventories.any(), ~Batch.id.in_(batches))).execute()

    Fingerprint.__table__.delete(Fingerprint.date < date).execute()
    Attendance.__table__.delete(Attendance.date < date).execute()