import datetime
from decimal import Decimal
import uuid
from pyramid.security import authenticated_userid
from pyramid.view import view_config
from sqlalchemy import func
import transaction
from brewman.models import DBSession
from brewman.models.master import LedgerBase, CostCenter, Product
from brewman.models.validation_exception import TryCatchFunction, ValidationError
from brewman.models.voucher import Journal, Voucher, VoucherType, Batch, Inventory

__author__ = 'tanshu'


@view_config(request_method='POST', route_name='api_reset_stock', renderer='json', permission='Reset Stock')
@TryCatchFunction
def rebase(request):
    user_id = uuid.UUID(authenticated_userid(request))
    product = Product.by_id(uuid.UUID(request.matchdict['id']))

    quantity = round(Decimal(request.GET['Quantity']), 2)
    stock_date = datetime.datetime.strptime(request.GET['StockDate'], '%d-%b-%Y')
    reset_date = datetime.datetime.strptime(request.GET['ResetDate'], '%d-%b-%Y')

    if reset_date > stock_date:
        raise ValidationError('Reset cannot be after the stock date')

    change = quantity - get_closing_stock(product, stock_date)
    if change == 0:
        return {'No Change Needed'}
    final = get_closing_stock(product)
    if final + change < 0:
        raise ValidationError('Current Quantity will get negative.  Cannot proceed')

    batch = get_last_batch(product)
    set_batches(batch, final + change)

    create_voucher(batch, change, reset_date, user_id)
    transaction.commit()
    return {}


def get_closing_stock(product, finish_date=None):
    query = DBSession.query(func.sum(Inventory.quantity * 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())
    if finish_date is not None:
        query = query.filter(Voucher.date <= finish_date)
    query = query.one()
    return 0 if query is None else query[0]


def get_last_batch(product):
    batch = DBSession.query(Batch).filter(Batch.product_id == product.id).order_by(Batch.name.desc()).first()
    if batch is None:
        raise ValidationError('Details for the product exist.  Just add a purchase entry')
    return batch


def set_batches(batch, quantity):
    batch.quantity_remaining = quantity
    batches = DBSession.query(Batch).filter(Batch.id != batch.id).filter(Batch.product_id == batch.product_id)
    for item in batches:
        item.quantity_remaining = 0
    pass


def create_voucher(batch, quantity, date, user_id):
    voucher = Voucher(date=date, narration='Product Reset', user_id=user_id, type=VoucherType.by_name('Issue'))
    DBSession.add(voucher)

    if quantity > 0:
        source = CostCenter.cost_center_overall()
        destination = CostCenter.cost_center_purchase()
    else:
        destination = CostCenter.cost_center_overall()
        source = CostCenter.cost_center_purchase()

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

    amount = round(inventory.amount, 2)
    source = Journal(debit=-1, ledger_id=LedgerBase.all_purchases(), amount=amount, cost_center_id=source)
    voucher.journals.append(source)
    DBSession.add(source)
    destination = Journal(debit=1, ledger_id=LedgerBase.all_purchases(), amount=amount, cost_center_id=destination)
    voucher.journals.append(destination)
    DBSession.add(destination)
