import psycopg2
import datetime
import json

from application import database_payloads, postsqldb
from application.recipes import database_recipes
from application.items import database_items
import config

def postTransaction(site_name, user_id, data: dict, conn=None):
    """ dict_keys(['item_id', 'logistics_info_id', 'barcode', 'item_name', 'transaction_type', 
     'quantity', 'description', 'cost', 'vendor', 'expires', 'location_id'])"""
    def quantityFactory(quantity_on_hand:float, quantity:float, transaction_type:str):
        if transaction_type == "Adjust In":
            quantity_on_hand += quantity
            return quantity_on_hand
        if transaction_type == "Adjust Out":
            quantity_on_hand -= quantity
            return quantity_on_hand
        raise Exception("The transaction type is wrong!")
    
    self_conn = False
    if not conn:
        database_config = config.config()
        conn = psycopg2.connect(**database_config)
        conn.autocommit = False
        self_conn = True


    transaction_time = datetime.datetime.now()

    cost_layer = postsqldb.CostLayerPayload(
        aquisition_date=transaction_time,
        quantity=float(data['quantity']),
        cost=float(data['cost']),
        currency_type="USD",
        vendor=int(data['vendor']),
        expires=data['expires']
    )
    transaction = postsqldb.TransactionPayload(
        timestamp=transaction_time,
        logistics_info_id=int(data['logistics_info_id']),
        barcode=data['barcode'],
        name=data['item_name'],
        transaction_type=data['transaction_type'],
        quantity=float(data['quantity']),
        description=data['description'],
        user_id=user_id,
    )
    

    location = database_recipes.selectItemLocationsTuple(site_name, (data['item_id'], data['location_id']), conn=conn)
    site_location = database_recipes.selectLocationsTuple(site_name, (location['location_id'], ), conn=conn)
    cost_layers: list = location['cost_layers']
    if data['transaction_type'] == "Adjust In":
        cost_layer = database_recipes.insertCostLayersTuple(site_name, cost_layer.payload(), conn=conn)
        cost_layers.append(cost_layer['id'])
    
    if data['transaction_type'] == "Adjust Out":
        if float(location['quantity_on_hand']) < float(data['quantity']):
            raise Exception(f"The quantity on hand for {data['item_name']} in {site_location['uuid']} is not enough to satisfy your transaction!")
        cost_layers = database_recipes.selectCostLayersTuple(site_name, payload=(location['id'], ))
        new_cost_layers = []
        qty = float(data['quantity'])
        for layer in cost_layers:
            if qty == 0.0:
                new_cost_layers.append(layer['id'])
            elif qty >= float(layer['quantity']):
                qty -= float(layer['quantity'])
                layer['quantity'] = 0.0
            else:
                layer['quantity'] -= qty
                new_cost_layers.append(layer['id'])
                database_recipes.updateCostLayersTuple(site_name, {'id': layer['id'], 'update': {'quantity': layer['quantity']}}, conn=conn)
                qty = 0.0
            
            if layer['quantity'] == 0.0:
                database_recipes.deleteCostLayersTuple(site_name, (layer['id'],), conn=conn)
        
        cost_layers = new_cost_layers

    quantity_on_hand = quantityFactory(float(location['quantity_on_hand']), data['quantity'], data['transaction_type'])

    updated_item_location_payload = (cost_layers, quantity_on_hand, data['item_id'], data['location_id'])
    database_recipes.updateItemLocation(site_name, updated_item_location_payload, conn=conn)
    #site_location = database_recipes.selectLocationsTuple(site_name, (location['location_id'], ), conn=conn)

    transaction.data = {'location': site_location['uuid']}

    transaction_tuple = database_recipes.insertTransactionsTuple(site_name, transaction.payload(), conn=conn)
    if self_conn:
        conn.commit()
        conn.close()
        return conn

    return conn

def process_recipe_receipt(site_name, user_id, data:dict, conn=None):
    """data={'recipe_id': recipe_id}"""

    self_conn = False
    if not conn:
        database_config = config.config()
        conn = psycopg2.connect(**database_config)
        conn.autocommit = False
        self_conn = True

    recipe = database_recipes.getRecipe(site_name, (data['recipe_id'],), conn=conn)

    sku_items = [rp_item for rp_item in recipe['recipe_items'] if rp_item['item_type'] == "sku"]
    for item in sku_items:
        rp_item_uom = item['uom']['id']
        item_stuff = database_recipes.selectItemTupleByUUID(site_name, (item['item_uuid'],), conn=conn)
        conv_factor = database_recipes.selectConversionTuple(site_name, (item_stuff['item_id'], rp_item_uom))
        conversion = conv_factor.get('conv_factor', 1)
        qty = float(item['qty']) / float(conversion)
        payload = {
            'item_id': item_stuff['item_id'],
            'logistics_info_id': item_stuff['logistics_info_id'],
            'barcode': "",
            'item_name': item_stuff['item_name'],
            'transaction_type': "Adjust Out",
            'quantity': qty,
            'description': f"Recipe Receipt - {data['recipe_id']}",
            'cost': 0.00,
            'vendor': 0,
            'expires': False,
            'location_id': item_stuff['auto_issue_location']
            }

        try:
            postTransaction(site_name, user_id, payload, conn=conn)
        except Exception as error:
            conn.commit()
            conn.close()
            return False, str(error)

    if self_conn:
        conn.commit()
        conn.close()

    return True, ""

def postNewSkuFromRecipe(site_name: str, user_id: int, data: dict, conn=None):
    """ data = {'name', 'subtype', 'qty', 'uom_id', 'main_link', 'cost'}"""
    self_conn = False
    if not conn:
        database_config = config.config()
        conn = psycopg2.connect(**database_config)
        conn.autocommit = False
        self_conn = True

    site = database_recipes.selectSiteTuple((site_name,))
    default_zone = database_recipes.getZone(site_name,(site['default_zone'], ))
    default_location = database_recipes.getLocation(site_name, (site['default_primary_location'],))
    uuid = f"{default_zone['name']}@{default_location['name']}"
    
    # create logistics info
    logistics_info = database_payloads.LogisticsInfoPayload(
            barcode=None, 
            primary_location=site['default_primary_location'],
            primary_zone=site['default_zone'],
            auto_issue_location=site['default_auto_issue_location'],
            auto_issue_zone=site['default_zone']
            )
    
    # create item info
    item_info = database_payloads.ItemInfoPayload(barcode=None, uom=data['uom_id'], cost=data['cost'])

    # create Food Info
    food_info = database_payloads.FoodInfoPayload()

    logistics_info_id = 0
    item_info_id = 0
    food_info_id = 0
    brand_id = 1

    
    logistics_info = database_recipes.insertLogisticsInfoTuple(site_name, logistics_info.payload(), conn=conn)
    item_info = database_recipes.insertItemInfoTuple(site_name, item_info.payload(), conn=conn)
    food_info = database_recipes.insertFoodInfoTuple(site_name, food_info.payload(), conn=conn)

    name = data['name']
    name = name.replace("'", "@&apostraphe&")
    links = {'main': data['main_link']}
    search_string = f"&&{name}&&"

    print(item_info)

    item = database_payloads.ItemsPayload(
        barcode=None, 
        item_name=data['name'], 
        item_info_id=item_info['id'],
        item_info_uuid=item_info['item_info_uuid'], 
        logistics_info_id=logistics_info['id'], 
        logistics_info_uuid=logistics_info['logistics_info_uuid'],
        food_info_id=food_info['id'],
        food_info_uuid=food_info['food_info_uuid'],
        links=links,
        brand=brand_id, 
        row_type="single", 
        item_type=data['subtype'], 
        search_string=search_string
        )

    item = database_recipes.insertItemTuple(site_name, item.payload(), conn=conn)
        
    with conn.cursor() as cur:
        cur.execute(f"SELECT id FROM {site_name}_locations WHERE uuid=%s;", (uuid, ))
        location_id = cur.fetchone()[0]

    database_payloads.ItemLocationPayload
    item_location = database_payloads.ItemLocationPayload(item['id'], location_id)
    database_recipes.insertItemLocationsTuple(site_name, item_location.payload(), conn=conn)


    creation_tuple = database_payloads.TransactionPayload(
            datetime.datetime.now(),
            logistics_info['id'],
            None,
            item['item_name'],
            "SYSTEM",
            0.0,
            "Item added to the System!",
            user_id,
            {'location': uuid}
        )

    database_recipes.insertTransactionsTuple(site_name, creation_tuple.payload(), conn=conn)

    item_uuid = item['item_uuid']

    if self_conn:
        conn.commit()
        conn.close()
        return False, item_uuid
    
    return conn, item_uuid
