import datetime
from sqlalchemy.sql.expression import func, desc

from pyramid.view import view_config

from brewman.models import DBSession
from brewman.models.master import CostCenter, LedgerType, LedgerBase

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

@view_config(request_method='GET', route_name='profit_loss', renderer='brewman:templates/angular_base.mako',
    permission='Profit & Loss')
def html(request):
    return {}


@view_config(request_method='GET', route_name='api_profit_loss', renderer='json', permission='Profit & Loss')
def report(request):
    start_date = request.GET.get('StartDate', None)
    finish_date = request.GET.get('FinishDate', None)
    if start_date and finish_date:
        report = {'StartDate': start_date, 'FinishDate': finish_date}
        start_date = datetime.datetime.strptime(start_date, '%d-%b-%Y')
        finish_date = datetime.datetime.strptime(finish_date, '%d-%b-%Y')

        net_profit, opening_stock, closing_stock,\
        report['ProfitLossBody'], report['ProfitLossFooter'] = build_profit_loss(request, start_date, finish_date)
        report['BalanceSheetBody'], report['BalanceSheetFooter'] = build_balance_sheet(request, net_profit,
            opening_stock, closing_stock, start_date, finish_date)
        return report
    else:
        return {'StartDate': session_period_start(request),
                'FinishDate': session_period_finish(request), 'Body': [], 'Footer': {}}


def build_profit_loss(request, start_date, finish_date):
    report = []
    groups = dict()
    type_list = []
    for item in LedgerType.list():
        if not item.balance_sheet:
            type_list.append(item.id)

    amount_sum = func.sum(Journal.amount * Journal.debit)
    query = DBSession.query(LedgerBase, amount_sum)\
    .join(Journal.voucher).join(Journal.ledger)\
    .filter(Voucher.date >= start_date)\
    .filter(Voucher.date <= finish_date)\
    .filter(Voucher.type != VoucherType.by_name('Issue').id)\
    .filter(LedgerBase.type.in_(type_list))\
    .group_by(LedgerBase).order_by(LedgerBase.type).order_by(desc(func.abs(amount_sum))).all()

    # Get opening / closing stock
    opening_stock, closing_stock = get_stocks(start_date, finish_date)
    opening_stock = 0 if opening_stock is None else opening_stock
    closing_stock = 0 if closing_stock is None else closing_stock
    total_amount = (opening_stock - closing_stock) * -1
    report.append(
        {'Name': 'Opening Stock', 'SubAmount': "\u20B9 {0:,.2f}".format(opening_stock * -1), 'Order': 20.0001})
    report.append({'Name': 'Closing Stock', 'SubAmount': "\u20B9 {0:,.2f}".format(closing_stock), 'Order': 29})
    purchase_group = LedgerType.by_id(2)
    groups[purchase_group.id] = {'Group': purchase_group.name, 'Amount': total_amount, 'Order': purchase_group.order}
    counter = 0
    for ledger, amount in query:
        # Add Items
        ledger_type = LedgerType.by_id(ledger.type)
        amount = amount * -1
        total_amount += amount
        if amount != 0:
            counter += .001
            report.append(
                {'Name': ledger.name, 'SubAmount': "\u20B9 {0:,.2f}".format(amount),
                 'Order': ledger_type.order + counter})
            if ledger_type.id in groups:
                groups[ledger_type.id]['Amount'] += amount
            else:
                groups[ledger_type.id] = {'Group': ledger_type.name, 'Amount': amount, 'Order': ledger_type.order}

    # Add Subtotals
    for item in groups.values():
        item['Amount'] = "\u20B9 {0:,.2f}".format(item['Amount'])
        report.append(item)

    footer = []
    # Add Net
    if total_amount > 0:
        footer.append({'Name': 'Net Profit', 'Amount': "\u20B9 {0:,.2f}".format(total_amount), 'Order': 100})
    else:
        footer.append({'Name': 'Net Loss', 'Amount': "\u20B9 {0:,.2f}".format(total_amount), 'Order': 100})

    return total_amount, opening_stock, closing_stock, sorted(report, key=lambda d: d['Order']), footer


def build_balance_sheet(request, net_profit, opening_stock, closing_stock, start_date, finish_date):
    report = []
    groups = dict()
    # Add Net Profit / Loss
    net_profit *= -1
    total_amount = net_profit
    report.append({'Name': 'Net Loss' if net_profit >= 0 else 'Net Profit',
                   'SubAmount': "\u20B9 {0:,.2f}".format(net_profit), 'Order': 79})

    # Add Accumulated Profit / Loss
    accumulated_profit = get_accumulated_profit(opening_stock, start_date)
    report.append({'Name': 'Accumulated Loss' if accumulated_profit >= 0 else 'Accumulated Profit',
                   'SubAmount': "\u20B9 {0:,.2f}".format(accumulated_profit), 'Order': 78})
    total_amount += accumulated_profit

    capital_group = LedgerType.by_id(5)
    groups[capital_group.id] = {'Group': capital_group.name, 'Amount': total_amount, 'Order': capital_group.order}

    total_amount += closing_stock
    report.append({'Name': 'Closing Stock', 'SubAmount': "\u20B9 {0:,.2f}".format(closing_stock), 'Order': 20.001})

    asset_group = LedgerType.by_id(4)
    groups[asset_group.id] = {'Group': asset_group.name, 'Amount': closing_stock, 'Order': asset_group.order}

    type_list = []
    for item in LedgerType.list():
        if item.balance_sheet:
            type_list.append(item.id)

    amount_sum = func.sum(Journal.amount * Journal.debit)
    query = DBSession.query(LedgerBase, amount_sum)\
    .join(Journal.voucher).join(Journal.ledger)\
    .filter(Voucher.date <= finish_date)\
    .filter(Voucher.type != VoucherType.by_name('Issue').id)\
    .filter(LedgerBase.type.in_(type_list))\
    .group_by(LedgerBase).order_by(LedgerBase.type).order_by(desc(func.abs(amount_sum))).all()

    counter = 0
    sss = 0
    for ledger, amount in query:
        # Add Items
        ledger_type = LedgerType.by_id(ledger.type)
        sss += amount
        total_amount += amount
        if amount != 0:
            counter += .001
            report.append(
                {'Name': ledger.name, 'SubAmount': "\u20B9 {0:,.2f}".format(amount),
                 'Order': ledger_type.order + counter})
            if ledger_type.id in groups:
                groups[ledger_type.id]['Amount'] += amount
            else:
                groups[ledger_type.id] = {'Group': ledger_type.name, 'Amount': amount, 'Order': ledger_type.order}

    # Add Subtotals
    for item in groups.values():
        item['Amount'] = "\u20B9 {0:,.2f}".format(item['Amount'])
        report.append(item)

    footer = [{'Name': 'Total', 'Amount': "\u20B9 {0:,.2f}".format(total_amount), 'Order': 100}]

    return sorted(report, key=lambda d: d['Order']), footer


def get_stocks(startDate, finishDate):
    opening_stock = DBSession.query(func.sum(Inventory.quantity * Inventory.rate * (1 + Inventory.tax) * Journal.debit))\
    .join(Journal.voucher).join(Journal.ledger).join(Voucher.inventories)\
    .filter(Voucher.date < startDate)\
    .filter(Journal.cost_center_id == CostCenter.cost_center_purchase())\
    .scalar()

    closing_stock = DBSession.query(func.sum(Inventory.quantity * Inventory.rate * (1 + Inventory.tax) * Journal.debit))\
    .join(Journal.voucher).join(Journal.ledger).join(Voucher.inventories)\
    .filter(Voucher.date <= finishDate)\
    .filter(Journal.cost_center_id == CostCenter.cost_center_purchase())\
    .scalar()

    return opening_stock, closing_stock


def get_accumulated_profit(opening_stock, start_date):
    type_list = []
    for item in LedgerType.list():
        if not item.balance_sheet:
            type_list.append(item.id)

    accumulated_profit = DBSession.query(func.sum(Journal.amount * Journal.debit))\
    .join(Journal.voucher).join(Journal.ledger)\
    .filter(Voucher.date < start_date)\
    .filter(Voucher.type != VoucherType.by_name('Issue').id)\
    .filter(LedgerBase.type.in_(type_list))\
    .scalar()
    if accumulated_profit is None:
        accumulated_profit = 0
    return accumulated_profit - opening_stock
