import datetime
from sqlalchemy.orm import joinedload
from sqlalchemy.sql.expression import func
import uuid

from pyramid.view import view_config

from brewman.models import DBSession
from brewman.models.master import Product, CostCenter
from brewman.models.validation_exception import ValidationError, TryCatchFunction

from brewman.models.voucher import Voucher, Journal, VoucherType, Inventory
from brewman.views import to_uuid
from brewman.views.services.session import session_period_start, session_period_finish
from brewman.views.services.voucher import get_edit_url


@view_config(request_method='GET', route_name='product_ledger_id', renderer='brewman:templates/angular_base.mako',
             permission='Product Ledger')
@view_config(request_method='GET', route_name='product_ledger', renderer='brewman:templates/angular_base.mako',
             permission='Product Ledger')
def html(request):
    return {}


@view_config(request_method='GET', route_name='api_product_ledger', renderer='json', permission='Product Ledger')
def show_blank(request):
    return {'StartDate': session_period_start(request),
            'FinishDate': session_period_finish(request), 'Product': None, 'Body': [], 'Footer': {}}


@view_config(request_method='GET', route_name='api_product_ledger_id', renderer='json', permission='Product Ledger')
@TryCatchFunction
def show_data(request):
    id = to_uuid(request.matchdict['id'])
    if id is None:
        raise ValidationError("Invalid Product")
    product = Product.by_id(id)
    start_date = request.GET.get('StartDate', session_period_start(request))
    finish_date = request.GET.get('FinishDate', session_period_finish(request))
    info = {'StartDate': start_date, 'FinishDate': finish_date,
            'Product': {'ProductID': product.id, 'Name': product.name},
            'Body': [], 'Footer': {}}
    build_report(request, info)
    return info


def build_report(request, info):
    product_id = info['Product']['ProductID']
    start_date = info['StartDate']
    finish_date = info['FinishDate']

    runningTotalQ, runningTotalA, opening = opening_balance(product_id, start_date)
    totalDebitQ = runningTotalQ
    totalDebitA = runningTotalA
    totalCreditQ = 0
    totalCreditA = 0

    info['Body'].append(opening)

    query = DBSession.query(Voucher, Inventory, Journal).options(
        joinedload(Journal.ledger, innerjoin=True),
        joinedload(Journal.costcenter, innerjoin=True)) \
        .filter(Voucher.id == Inventory.voucher_id) \
        .filter(Voucher.id == Journal.voucher_id) \
        .filter(Inventory.product_id == product_id) \
        .filter(Journal.cost_center_id != CostCenter.cost_center_purchase()) \
        .filter(Voucher.date >= datetime.datetime.strptime(start_date, '%d-%b-%Y')) \
        .filter(Voucher.date <= datetime.datetime.strptime(finish_date, '%d-%b-%Y')) \
        .order_by(Voucher.date).order_by(Voucher.last_edit_date).all()

    for row in query:
        journalDebit = row.Journal.debit * -1
        name = row.Journal.costcenter.name if row.Voucher.type == VoucherType.by_name(
            'Issue').id else row.Journal.ledger.name
        debitQ = row.Inventory.quantity if journalDebit == 1 else 0
        debitA = row.Inventory.amount if journalDebit == 1 else 0
        creditQ = row.Inventory.quantity if journalDebit != 1 else 0
        creditA = row.Inventory.amount if journalDebit != 1 else 0

        runningTotalQ += (row.Inventory.quantity * journalDebit)
        runningTotalA += (row.Inventory.amount * journalDebit)

        if journalDebit == 1:
            totalDebitQ += row.Inventory.quantity
            totalDebitA += row.Inventory.amount
        else:
            totalCreditQ += row.Inventory.quantity
            totalCreditA += row.Inventory.amount

        info['Body'].append(
            {'Date': row.Voucher.date.strftime('%d-%b-%Y'), 'Name': name, 'Url': get_edit_url(request, row.Voucher),
             'Type': VoucherType.by_id(row.Voucher.type).name, 'Narration': row.Voucher.narration, 'DebitQ': debitQ,
             'DebitA': debitA, 'CreditQ': creditQ, 'CreditA': creditA, 'RunningQ': runningTotalQ,
             'RunningA': runningTotalA})

    info['Footer'] = {'Date': finish_date, 'Name': 'Closing Balance', 'Type': 'Closing Balance', 'Narration': '',
                      'DebitQ': totalDebitQ, 'DebitA': totalDebitA, 'CreditQ': totalCreditQ, 'CreditA': totalCreditA,
                      'RunningQ': runningTotalQ, 'RunningA': runningTotalA}


def opening_balance(product_id, start_date):
    quantity, amount = DBSession.query(func.sum(Inventory.quantity * Journal.debit),
                                       func.sum(Inventory.amount * Journal.debit)) \
        .join(Voucher) \
        .filter(Voucher.id == Inventory.voucher_id) \
        .filter(Voucher.id == Journal.voucher_id) \
        .filter(Inventory.product_id == product_id) \
        .filter(Journal.cost_center_id == CostCenter.cost_center_purchase()) \
        .filter(Voucher.date < datetime.datetime.strptime(start_date, '%d-%b-%Y')) \
        .one()

    if quantity and quantity > 0:
        debitQuantity = quantity
        debitAmount = amount
    else:
        debitQuantity = ''
        debitAmount = ''

    if quantity is None:
        quantity = 0
        amount = 0

    return quantity, amount, {'Date': start_date, 'Name': 'Opening Balance', 'Type': 'Opening Balance', 'Narration': '',
                              'DebitQ': debitQuantity, 'DebitA': debitAmount, 'RunningQ': quantity, 'RunningA': amount}

