import datetime
from decimal import Decimal
import uuid
from brewman.models import DBSession
from brewman.models.master import Product, LedgerBase
from brewman.models.operations import inventory_valid, journals_valid
from brewman.models.validation_exception import ValidationError
from brewman.models.voucher import Voucher, VoucherType, Batch, Inventory, Journal

__author__ = 'tanshu'


def purchase_return_create_voucher(json, user):
    dt = datetime.datetime.strptime(json['Date'], '%d-%b-%Y')
    voucher = Voucher(date=dt, narration=json['Narration'], user_id=user.id, type=VoucherType.by_name(json['Type']))
    DBSession.add(voucher)

    for item in json['Inventories']:
        purchase_return_create_inventory(voucher, item)
    for item in purchase_return_create_journals(voucher.inventories, json['Journals'][0]['Ledger']['LedgerID']):
        voucher.journals.append(item)
        DBSession.add(item)
    journals_valid(voucher)
    inventory_valid(voucher)
    return voucher


def purchase_return_create_inventory(voucher, item):
    batch = Batch.by_id(uuid.UUID(item['Batch']['BatchID']))
    inventory_id = uuid.UUID(item['InventoryID']) if 'InventoryID' in item else None
    quantity = Decimal(item['Quantity'])

    if quantity <= 0:
        raise ValidationError("Quantity of {0} cannot be zero".format(item.product.name))
    if quantity > batch.quantity_remaining:
        raise ValidationError("Quantity available is {0} only".format(batch.quantity_remaining))
    if batch.name > voucher.date:
        raise ValidationError("Batch of {0} was purchased after the issue date".format(batch.product.name))

    batch.quantity_remaining -= quantity

    item = Inventory(id=inventory_id, product_id=batch.product.id, quantity=quantity, rate=batch.rate, tax=batch.tax,
                     discount=batch.discount, batch=batch)
    voucher.inventories.append(item)
    DBSession.add(item)


def purchase_return_create_journals(inventories, ledgerID):
    otherLedger = LedgerBase.by_id(uuid.UUID(ledgerID))
    journals = dict()
    amount = 0
    for item in inventories:
        ledger = Product.by_id(item.product_id).ledger
        amount += round(item.amount, 2)
        if ledger.id in journals:
            journals[ledger.id].amount += round(item.amount, 2)
        else:
            journals[ledger.id] = Journal(debit=-1, cost_center_id=ledger.costcenter_id, ledger_id=ledger.id,
                                          amount=round(item.amount, 2))
    journals[otherLedger.id] = Journal(debit=1, cost_center_id=otherLedger.costcenter_id, ledger_id=otherLedger.id,
                                       amount=amount)
    return list(journals.values())


def purchase_return_update_voucher(voucher, json, user):
    voucher.date = datetime.datetime.strptime(json['Date'], '%d-%b-%Y')
    voucher.narration = json['Narration']
    voucher.user_id = user.id
    voucher.posted = False
    voucher.last_edit_date = datetime.datetime.utcnow()

    purchase_return_update_inventory(voucher, json['Inventories'], json['Date'])
    purchase_return_update_journals(voucher, json['Journals'])
    journals_valid(voucher)
    inventory_valid(voucher)
    return voucher


def purchase_return_update_inventory(voucher, newInventories, date):
    for it in range(len(voucher.inventories), 0, -1):
        item = voucher.inventories[it - 1]
        found = False
        for j in range(len(newInventories), 0, -1):
            i = newInventories[j - 1]
            if 'InventoryID' in i and item.id == uuid.UUID(i['InventoryID']):
                product = Product.by_id(uuid.UUID(i['Product']['ProductID']))
                found = True
                if item.product_id != product.id:
                    raise ValidationError('Product cannot be changed')
                quantity = round(Decimal(i['Quantity']), 2)
                if quantity == 0:
                    raise ValidationError("Quantity of {0} cannot be zero".format(item.product.name))
                if quantity - item.quantity > item.batch.quantity_remaining:
                    raise ValidationError("Maximum quantity available for {0} is {1}".format(item.product.full_name,
                                                                                             item.quantity + item.batch.quantity_remaining))
                if item.batch.name > voucher.date:
                    raise ValidationError("Batch of {0} was purchased after the issue date".format(item.product.name))
                item.batch.quantity_remaining -= (quantity - item.quantity)
                item.quantity = quantity
                newInventories.remove(i)
                break
        if not found:
            item.batch.quantity_remaining += item.quantity
            voucher.inventories.remove(item)
            DBSession.delete(item)
    for i in newInventories:
        purchase_return_create_inventory(voucher, i)


def purchase_return_update_journals(voucher, journals):
    otherLedger = [ff for ff in journals if ff['Debit'] == 1]
    otherLedger = LedgerBase.by_id(uuid.UUID(otherLedger[0]['Ledger']['LedgerID']))
    journals = dict()
    amount = 0
    for item in voucher.inventories:
        ledger = Product.by_id(item.product_id).ledger
        amount += round(item.amount, 2)
        if ledger.id in journals:
            journals[ledger.id].amount += round(item.amount, 2)
        else:
            journals[ledger.id] = Journal(debit=-1, cost_center_id=ledger.costcenter_id, ledger_id=ledger.id,
                                          amount=round(item.amount, 2))
    journals[otherLedger.id] = Journal(debit=1, cost_center_id=otherLedger.costcenter_id, ledger_id=otherLedger.id,
                                       amount=amount)
    for i in range(len(voucher.journals), 0, -1):
        item = voucher.journals[i - 1]
        if item.ledger_id in journals:
            item.debit = journals[item.ledger_id].debit
            item.amount = journals[item.ledger_id].amount
            item.cost_center_id = journals[item.ledger_id].cost_center_id
            del journals[item.ledger_id]
        else:
            DBSession.delete(item)
            voucher.journals.remove(item)
    for item in journals.values():
        item.amount = item.amount
        voucher.journals.append(item)
        DBSession.add(item)


