import uuid
from pyramid.response import Response
from pyramid.security import authenticated_userid

from pyramid.view import view_config
from sqlalchemy.orm import joinedload_all
import transaction
from brewman import groupfinder
from brewman.models import DBSession

from brewman.models.master import CostCenter, Ledger, LedgerType, LedgerBase
from brewman.models.validation_exception import ValidationError, TryCatchFunction
from brewman.models.voucher import Voucher, Journal, VoucherType


@view_config(route_name='account_list', renderer='brewman:templates/angular_base.mako', permission='Accounts')
@view_config(request_method='GET', route_name='account_id', renderer='brewman:templates/angular_base.mako',
             permission='Accounts')
@view_config(request_method='GET', route_name='account', renderer='brewman:templates/angular_base.mako',
             permission='Accounts')
def html(request):
    return {}


@view_config(request_method='POST', route_name='api_account', renderer='json', permission='Accounts')
@TryCatchFunction
def save(request):
    item = Ledger(code=0, name=request.json_body['Name'], type=int(request.json_body['Type']),
                  is_active=request.json_body['IsActive'], is_reconcilable=request.json_body['IsReconcilable'],
                  costcenter_id=uuid.UUID(request.json_body['CostCenter']['CostCenterID'])).create()
    transaction.commit()
    return account_info(item.id)


@view_config(request_method='POST', route_name='api_account_id', renderer='json', permission='Accounts')
@TryCatchFunction
def update(request):
    item = Ledger.by_id(uuid.UUID(request.matchdict['id']))
    if item.is_fixture:
        raise ValidationError("{0} is a fixture and cannot be edited or deleted.".format(item.name))
    new_type = int(request.json_body['Type'])
    if not item.type == new_type:
        item.code = Ledger.get_code(new_type)
        item.type = new_type
    item.name = request.json_body['Name']
    item.is_active = request.json_body['IsActive']
    item.is_reconcilable = request.json_body['IsReconcilable']
    item.costcenter_id = uuid.UUID(request.json_body['CostCenter']['CostCenterID'])
    transaction.commit()
    return account_info(item.id)


@view_config(request_method='DELETE', route_name='api_account_id', renderer='json', permission='Accounts')
def delete(request):
    account = Ledger.by_id(uuid.UUID(request.matchdict['id']))
    can_delete, reason = account.can_delete('Advanced Delete' in groupfinder(authenticated_userid(request), request))
    if can_delete:
        delete_with_data(account)
        transaction.commit()
        return account_info(None)
    else:
        transaction.abort()
        response = Response("Cannot delete account because {0}".format(reason))
        response.status_int = 500
        return response


@view_config(request_method='GET', route_name='api_account_id', renderer='json', permission='Accounts')
def show_id(request):
    return account_info(uuid.UUID(request.matchdict.get('id', None)))


@view_config(request_method='GET', route_name='api_account', renderer='json', permission='Accounts')
def show_blank(request):
    return account_info(None)


@view_config(request_method='GET', route_name='api_account', renderer='json', request_param='list',
             permission='Authenticated')
def show_list(request):
    list = Ledger.list()
    ledgers = []
    for item in list:
        ledgers.append({'Name': item.name, 'Type': item.type_object.name, 'IsActive': 'Yes' if item.is_active else 'No',
                        'IsReconcilable': 'Yes' if item.is_reconcilable else '', 'CostCenter': item.costcenter.name,
                        'IsFixture': item.is_fixture, 'Url': request.route_url('account_id', id=item.id)})
    return ledgers


@view_config(request_method='GET', route_name='api_account', renderer='json', request_param='term',
             permission='Authenticated')
def show_term(request):
    type = request.GET.get('type', None)
    type = int(type) if type is not None else None
    filter = request.GET.get('term', None)
    filter = filter if filter is not None and filter is not '' else None
    reconcilable = request.GET.get('r', None)
    reconcilable = reconcilable if reconcilable is not None else None
    active = request.GET.get('a', None)
    active = active if active is not None else None
    count = request.GET.get('count', None)
    count = None if count is None or count == '' else int(count)

    list = []
    for index, item in enumerate(LedgerBase.list(type, filter, reconcilable, active)):
        list.append({'LedgerID': item.id, 'Name': item.name})
        if count is not None and index == count - 1:
            break
    return list


@view_config(request_method='GET', route_name='api_account_type_list', renderer='json', permission='Authenticated')
def account_type_list(request):
    account_types = []
    for item in LedgerType.list():
        account_types.append({'AccountTypeID': item.id, 'Name': item.name})
    return account_types


def account_info(id):
    if id is None:
        account = {'Code': '(Auto)', 'Type': LedgerType.by_name('Creditors').id, 'IsActive': True,
                   'IsReconcilable': False, 'CostCenter': CostCenter.overall()}
    else:
        account = Ledger.by_id(id)
        account = {'LedgerID': account.id, 'Code': account.code, 'Name': account.name, 'Type': account.type,
                   'IsActive': account.is_active, 'IsReconcilable': account.is_reconcilable,
                   'IsFixture': account.is_fixture,
                   'CostCenter': {'CostCenterID': account.costcenter_id, 'Name': account.costcenter.name}}
    return account


def delete_with_data(account):
    suspense_ledger = Ledger.by_id(Ledger.suspense())
    query = Voucher.query().options(joinedload_all(Voucher.journals, Journal.ledger, innerjoin=True)) \
        .filter(Voucher.journals.any(Journal.ledger_id == account.id)) \
        .all()

    for voucher in query:
        others, sus_jnl, acc_jnl = False, None, None
        for journal in voucher.journals:
            if journal.ledger_id == account.id:
                acc_jnl = journal
            elif journal.ledger_id == Ledger.suspense():
                sus_jnl = journal
            else:
                others = True
        if not others:
            DBSession.delete(voucher)
        else:
            if sus_jnl is None:
                acc_jnl.ledger = suspense_ledger
                voucher.narration += '\nSuspense \u20B9{0:,.2f} is {1}'.format(acc_jnl.amount, account.name)
            else:
                amount = (sus_jnl.debit * sus_jnl.amount) + (acc_jnl.debit * acc_jnl.amount)
                DBSession.delete(acc_jnl)
                if amount == 0:
                    DBSession.delete(sus_jnl)
                else:
                    sus_jnl.amount = abs(amount)
                    sus_jnl.debit = -1 if amount < 0 else 1
                voucher.narration += '\nDeleted \u20B9{0:,.2f} of {1}'.format(acc_jnl.amount * acc_jnl.debit,
                                                                               account.name)
            if voucher.type in (VoucherType.by_name('Payment').id, VoucherType.by_name('Receipt').id):
                voucher.type = VoucherType.by_name('Journal')
    DBSession.delete(account)
