import uuid
from pyramid.response import Response
from pyramid.security import authenticated_userid
from pyramid.view import view_config
from sqlalchemy import func
import transaction
from brewman import groupfinder
from brewman.models import DBSession
from brewman.models.auth import User
from brewman.models.master import LedgerBase, CostCenter, DbSetting
from brewman.models.validation_exception import ValidationError, TryCatchFunction
from brewman.models.voucher import Voucher, VoucherType, Batch, Inventory
from brewman.views import get_lock_info
from .issue import issue_create_voucher, issue_update_voucher
from .journal import journal_update_voucher, journal_create_voucher
from .purchase import purchase_create_voucher, purchase_update_voucher

__author__ = 'tanshu'


@view_config(request_method='GET', route_name='journal_id', renderer='brewman:templates/angular_base.mako',
             permission='Journal')
@view_config(request_method='GET', route_name='journal', renderer='brewman:templates/angular_base.mako',
             permission='Journal')
@view_config(request_method='GET', route_name='payment_id', renderer='brewman:templates/angular_base.mako',
             permission='Payment')
@view_config(request_method='GET', route_name='payment', renderer='brewman:templates/angular_base.mako',
             permission='Payment')
@view_config(request_method='GET', route_name='receipt_id', renderer='brewman:templates/angular_base.mako',
             permission='Receipt')
@view_config(request_method='GET', route_name='receipt', renderer='brewman:templates/angular_base.mako',
             permission='Receipt')
@view_config(request_method='GET', route_name='purchase_id', renderer='brewman:templates/angular_base.mako',
             permission='Purchase')
@view_config(request_method='GET', route_name='purchase', renderer='brewman:templates/angular_base.mako',
             permission='Purchase')
@view_config(request_method='GET', route_name='purchase_return_id', renderer='brewman:templates/angular_base.mako',
             permission='Purchase Return')
@view_config(request_method='GET', route_name='purchase_return', renderer='brewman:templates/angular_base.mako',
             permission='Purchase Return')
@view_config(request_method='GET', route_name='issue_id', renderer='brewman:templates/angular_base.mako',
             permission='Issue')
@view_config(request_method='GET', route_name='issue', renderer='brewman:templates/angular_base.mako',
             permission='Issue')
@view_config(request_method='GET', route_name='salary_deduction_id', renderer='brewman:templates/angular_base.mako',
             permission='Issue')
@view_config(request_method='GET', route_name='salary_deduction', renderer='brewman:templates/angular_base.mako',
             permission='Issue')
def journal_get(request):
    return {}


@view_config(request_method='POST', route_name='api_voucher_id', request_param='post', renderer='json',
             permission='Post Vouchers')
@TryCatchFunction
def voucher_post(request):
    user = User.by_id(uuid.UUID(authenticated_userid(request)))
    voucher = Voucher.by_id(uuid.UUID(request.matchdict['id']))
    start, finish = get_lock_info()
    if start is not None and start > voucher.date:
        raise ValidationError("Vouchers before {0} have been locked.".format(start.strftime('%d-%b-%Y')))
    elif finish is not None and finish < voucher.date:
        raise ValidationError("Vouchers after {0} have been locked.".format(finish.strftime('%d-%b-%Y')))
    voucher.posted = True
    voucher.poster_id = user.id
    transaction.commit()
    return voucher_info(Voucher.by_id(voucher.id))


def check_delete_permissions(request, voucher):
    user = User.by_id(uuid.UUID(authenticated_userid(request)))
    permissions = groupfinder(user.id, request)

    if voucher.posted and not 'Edit Posted Vouchers' in permissions:
        response = Response("You are not allowed to edit posted vouchers")
        response.status_int = 403
        return response
    elif voucher.user_id != user.id and "Edit Other User's Vouchers" not in permissions:
        response = Response("You are not allowed to edit other user's vouchers")
        response.status_int = 403
        return response
    elif VoucherType.by_id(voucher.type).name not in permissions:
        response = Response("You are not allowed (0) vouchers".format(VoucherType.by_id(voucher.type).name))
        response.status_int = 403
        return response
    start, finish = get_lock_info()
    if start is not None and start > voucher.date:
        response = Response("Vouchers before {0} have been locked.".format(start.strftime('%d-%b-%Y')))
        response.status_int = 403
        return response
    elif finish is not None and finish < voucher.date:
        response = Response("Vouchers after {0} have been locked.".format(finish.strftime('%d-%b-%Y')))
        response.status_int = 403
        return response


@view_config(request_method='DELETE', route_name='api_voucher_id', renderer='json')
@TryCatchFunction
def delete(request):
    voucher = Voucher.by_id(uuid.UUID(request.matchdict['id']))
    permission = check_delete_permissions(request, voucher)
    if permission is not None:
        return permission
    json_voucher = voucher_info(voucher)
    batches_to_delete = []
    if voucher.type == VoucherType.by_name('Issue').id:
        for item in voucher.journals:
            if item.debit == 1:
                destination = item.cost_center_id
            else:
                source = item.cost_center_id
        batch_consumed = True if source == CostCenter.cost_center_purchase() else False if destination == CostCenter.cost_center_purchase() else None
        if batch_consumed is None:
            pass
        elif batch_consumed:
            for item in voucher.inventories:
                item.batch.quantity_remaining += item.quantity
        else:
            for item in voucher.inventories:
                if item.batch.quantity_remaining < item.batch.quantity_remaining:
                    raise ValueError(
                        'Quantity remaining for {0} is less than issued, hence cannot be deleted'.format(
                            item.product.name))
                item.batch.quantity_remaining -= item.quantity
    elif voucher.type == VoucherType.by_name('Purchase').id:
        for item in voucher.inventories:
            uses = DBSession.query(func.count(Inventory.id)) \
                .filter(Inventory.batch_id == item.batch.id) \
                .filter(Inventory.id != item.id).scalar()
            if uses > 0:
                raise ValueError('{0} has been issued and cannot be deleted'.format(item.product.name))
            batches_to_delete.append(item.batch)
    elif voucher.type == VoucherType.by_name('Purchase Return').id:
        for item in voucher.inventories:
            item.batch.quantity_remaining += item.quantity
    for b in batches_to_delete:
        DBSession.delete(b)
    DBSession.delete(voucher)
    transaction.commit()
    return blank_voucher(info=json_voucher)


@view_config(request_method='GET', route_name='api_voucher_id', renderer='json')
@TryCatchFunction
def get_old(request):
    id = request.matchdict.get('id', None)
    return voucher_info(Voucher.by_id(uuid.UUID(id)))


def voucher_info(voucher):
    json_voucher = {'VoucherID': voucher.id,
                    'Date': voucher.date.strftime('%d-%b-%Y'),
                    'Type': VoucherType.by_id(voucher.type).name,
                    'Posted': voucher.posted,
                    'Narration': voucher.narration,
                    'Journals': [],
                    'Inventories': [],
                    'SalaryDeductions': [],
                    'CreationDate': voucher.creation_date.strftime('%d-%b-%Y %H:%M'),
                    'LastEditDate': voucher.last_edit_date.strftime('%d-%b-%Y %H:%M'),
                    'User': {'UserID': voucher.user.id, 'Name': voucher.user.name},
                    'Poster': voucher.poster.name if voucher.posted else ''}
    if voucher.reconcile_date is not None:
        json_voucher['ReconcileDate'] = voucher.reconcile_date.strftime('%d-%b-%Y %H:%M')
    for item in voucher.journals:
        json_voucher['Journals'].append({'JournalID': item.id, 'Debit': item.debit, 'Amount': item.amount,
                                         'Ledger': {'LedgerID': item.ledger.id, 'Name': item.ledger.name},
                                         'CostCenter': {'CostCenterID': item.cost_center_id}})
    for item in voucher.salary_deductions:
        json_voucher['SalaryDeductions'].append(
            {'GrossSalary': item.gross_salary,
             'DaysWorked': item.days_worked,
             'EsiEmployee': item.esi_ee,
             'PfEmployee': item.pf_ee,
             'EsiEmployer': item.esi_er,
             'PfEmployer': item.pf_er,
             'Journal': {'JournalID': item.journal.id,
                         'Ledger': {'LedgerID': item.journal.ledger.id, 'Name': item.journal.ledger.name,
                                    'Designation': item.journal.ledger.designation,
                                    'CostCenter': {'CostCenterID': item.journal.ledger.costcenter.id,
                                                   'Name': item.journal.ledger.costcenter.name}}}})
    for item in voucher.inventories:
        text = "{0} ({1}) {2:.2f}@{3:.2f} from {4}".format(item.product.name, item.product.units,
            item.batch.quantity_remaining, item.batch.rate, item.batch.name.strftime('%d-%b-%Y'))
        json_voucher['Inventories'].append(
            {'InventoryID': item.id, 'Quantity': item.quantity, 'Rate': item.rate,
             'Tax': item.tax, 'Discount': item.discount, 'Amount': item.amount,
             'Product': {'ProductID': item.product.id, 'Name': item.product.full_name, 'Units': item.product.units,
                         'Price': item.rate},
             'Batch': {'BatchID': item.batch.id, 'Name': text,
                       'QuantityRemaining': item.batch.quantity_remaining, 'Tax': item.batch.tax,
                       'Discount': item.batch.discount, 'Rate': item.batch.rate,
                       'Product': {'ProductID': item.batch.product.id, 'Name': item.batch.product.full_name}}})
    return json_voucher


def blank_voucher(info):
    if 'Type' not in info:
        raise ValidationError('Voucher Type is null')
    type = info['Type']
    if 'Date' not in info:
        raise ValidationError('Date cannot be null')
    json_voucher = {'Type': type, 'Date': info['Date'], 'Posted': False, 'Narration': "", 'Journals': [], 'Inventories': []}
    if type == 'Journal':
        pass
    elif type == 'Payment':
        ledger = None
        if info is not None and 'Account' in info and info['Account'] is not None:
            ledger = LedgerBase.by_id(info['Account'])
        if ledger is not None:
            ledger = {'LedgerID': ledger.id, 'Name':ledger.name}
        else:
            ledger = LedgerBase.cash_in_hand()
        json_voucher['Journals'].append({'Ledger': ledger, 'Amount': 0, 'Debit': -1})
    elif type == 'Receipt':
        ledger = None
        if info is not None and 'Account' in info and info['Account'] is not None:
            ledger = LedgerBase.by_id(info['Account'])
        if ledger is not None:
            ledger = {'LedgerID': ledger.id, 'Name':ledger.name}
        else:
            ledger = LedgerBase.cash_in_hand()
        json_voucher['Journals'].append({'Ledger': ledger, 'Amount': 0, 'Debit': 1})
    elif type == 'Purchase':
        json_voucher['Journals'].append({'Ledger': LedgerBase.local_purchase(), 'Amount': 0, 'Debit': -1})
    elif type == 'Purchase Return':
        json_voucher['Journals'].append({'Ledger': LedgerBase.local_purchase(), 'Amount': 0, 'Debit': 1})
    elif type == 'Issue':
        if 'Source' in info and 'Destination' in info:
            json_voucher['Journals'].append({'Ledger': {'LedgerID': LedgerBase.all_purchases()},
                                             'Amount': 0,
                                             'Debit': -1,
                                             'CostCenter': {'CostCenterID': info['Source']}})
            json_voucher['Journals'].append({'Ledger': {'LedgerID': LedgerBase.all_purchases()},
                                             'Amount': 0,
                                             'Debit': 1,
                                             'CostCenter': {'CostCenterID': info['Destination']}})
        elif 'Journals' not in info:
            json_voucher['Journals'].append({'Ledger': {'LedgerID': LedgerBase.all_purchases()},
                                             'Amount': 0,
                                             'Debit': -1,
                                             'CostCenter': {'CostCenterID': CostCenter.cost_center_purchase()}})
            json_voucher['Journals'].append({'Ledger': {'LedgerID': LedgerBase.all_purchases()},
                                             'Amount': 0,
                                             'Debit': 1,
                                             'CostCenter': {'CostCenterID': CostCenter.cost_center_kitchen()}})
        else:
            json_voucher['Date'] = info['Date']
            for item in info['Journals']:
                json_voucher['Journals'].append({'Ledger': {'LedgerID': item['Ledger']['LedgerID']},
                                                 'Amount': 0,
                                                 'Debit': item['Debit'],
                                                 'CostCenter': {'CostCenterID': item['CostCenter']['CostCenterID']}})

    elif type == 'Salary Deduction':
        json_voucher['SalaryDeductions'] = []
    else:
        raise ValidationError("Voucher of type \"{0}\" does not exist.".format(type))
    return json_voucher


def get_edit_url(request, voucher):
    if voucher.type == 1:
        return request.route_url('journal_id', id=voucher.id)
    elif voucher.type == 2:
        return request.route_url('purchase_id', id=voucher.id)
    elif voucher.type == 3:
        return request.route_url('issue_id', id=voucher.id)
    elif voucher.type == 4:
        return request.route_url('payment_id', id=voucher.id)
    elif voucher.type == 5:
        return request.route_url('receipt_id', id=voucher.id)
    elif voucher.type == 6:
        return request.route_url('purchase_return_id', id=voucher.id)
    elif voucher.type == 12:
        return request.route_url('salary_deduction_id', id=voucher.id)
    else:
        return '#'
