from flask import Blueprint, request, render_template, redirect, session, url_for, send_file, jsonify, Response
import psycopg2, math, json, datetime, main, copy, requests, process, database, pprint, MyDataclasses
from config import config, sites_config
from main import unfoldCostLayers
from user_api import login_required
import application.postsqldb as db
from application.items import database_items
from application.items import items_processes

items_api = Blueprint('items_api', __name__)

@items_api.route("/item/<parent_id>/itemLink/<id>")
@login_required
def itemLink(parent_id, id):
    sites = [site[1] for site in main.get_sites(session['user']['sites'])]
    return render_template("items/itemlink.html", current_site=session['selected_site'], sites=sites, proto={'referrer': request.referrer}, id=id)

@items_api.route("/item/getTransactions", methods=["GET"])
@login_required
def getTransactions():
    """ GET a subquery of transactions by passing a logistics_info_id, limit, and page
    ---
    responses:
        200:
            description: transactions received successfully.
    """
    if request.method == "GET":
        recordset = []
        count = 0
        logistics_info_id = int(request.args.get('logistics_info_id', 1))
        page = int(request.args.get('page', 1))
        limit = int(request.args.get('limit', 50))
        site_name = session['selected_site']
        offset = (page - 1) * limit
        recordset, count = database_items.getTransactions(site_name, (logistics_info_id, limit, offset))
        return jsonify({"transactions": recordset, "end": math.ceil(count/limit), "error": False, "message": ""})
    return jsonify({"transactions": recordset, "end": math.ceil(count/limit), "error": True, "message": f"method {request.method} is not allowed."})

@items_api.route("/item/getTransaction", methods=["GET"])
@login_required
def getTransaction():
    """ GET a transaction from the system by passing an ID
    ---
    parameters:
        - in: query
          name: id
          schema:
            type: integer
            minimum: 1
            default: 1
          required: true
          description: The transaction.id
    responses:
        200:
            description: Transaction Object received successfully.
    """
    transaction = ()
    if request.method == "GET":
        id = int(request.args.get('id', 1))
        site_name = session['selected_site']
        transaction = database_items.getTransaction(site_name, (id, ))
        return jsonify({"transaction": transaction, "error": False, "message": ""})
    return jsonify({"transaction": transaction,  "error": True, "message": f"method {request.method} is not allowed."})

@items_api.route("/item/getItem", methods=["GET"])
@login_required
def get_item():
    """ GET item from system by passing its ID
    ---
    parameters:
        - in: query
          name: id
          schema:
            type: integer
            minimum: 1
            default: 1
          description: item.id
    responses:
        200:
            description: Item.id received successfully!
    """
    if request.method == "GET":
        id = int(request.args.get('id', 1))
        site_name = session['selected_site']
        item = ()
        item = database_items.getItemAllByID(site_name, (id, ))
        return jsonify({'item': item, 'error': False, 'message': ''})
    return jsonify({'item': item, 'error': True, 'message': f'method {request.method} not allowed.'})

@items_api.route("/item/getItemsWithQOH", methods=['GET'])
@login_required
def pagninate_items():
    """ GET items from the system by passing a page, limit, search_string, sort, and order
    ---
    parameters:
        - in: query
          name: page
          schema:
            type: integer
            default: 1
          description: page number for offset
        - in: query
          name: limit
          schema:
            type: integer
            default: 50
          description: number of records to grab
        - in: query
          name: search_string
          schema:
            type: string
            default: ''
          description: string to look for in column search_string
        - in: query
          name: sort
          schema:
            type: string
            default: ''
          description: items table column to sort by
        - in: query
          name: order
          schema:
            type: string
            enum: ['ASC', 'DESC']
            default: 'ASC'
          description: Order to sort items table sort parameter by
    responses:
        200:
            description: Items received successfully.
    """
    items = []
    count = 0
    if request.method == "GET":
        page = int(request.args.get('page', 1))
        limit = int(request.args.get('limit', 10))
        search_string = str(request.args.get('search_text', ""))
        sort = request.args.get('sort', "")
        order = request.args.get('order', "")
        site_name = session['selected_site']
        offset = (page - 1) * limit
        if sort == 'total_qoh':
            sort_order = f"{sort} {order}"
        else:
            sort_order = f"item.{sort} {order}"
        
        items, count = database_items.getItemsWithQOH(site_name, (search_string, limit, offset, sort_order))
        
        return jsonify({'items': items, "end": math.ceil(count/limit), 'error':False, 'message': 'Items Loaded Successfully!'})
    return jsonify({'items': items, "end": math.ceil(count/limit), 'error':True, 'message': 'There was a problem loading the items!'})

@items_api.route('/item/getModalItems', methods=["GET"])
@login_required
def getModalItems():
    """ GET items from the system by passing a page, limit, search_string. For select modals
    ---
    parameters:
        - in: query
          name: page
          schema:
            type: integer
            default: 1
          description: page number for offset
        - in: query
          name: limit
          schema:
            type: integer
            default: 25
          description: number of records to grab
        - in: query
          name: search_string
          schema:
            type: string
            default: ''
          description: string to look for in column search_string  
    responses:
        200:
            description: Items received successfully.
    """
    recordset, count = tuple(), 0
    if request.method == "GET":
        page = int(request.args.get('page', 1))
        limit = int(request.args.get('limit', 10))
        search_string = request.args.get('search_string', '')
        site_name = session['selected_site']
        offset = (page - 1) * limit
        recordset, count = database_items.getModalSKUs(site_name, (search_string, limit, offset))
        print(recordset, count)
        return jsonify({"items":recordset, "end":math.ceil(count/limit), "error":False, "message":"items fetched succesfully!"})
    return jsonify({"items":recordset, "end":math.ceil(count/limit), "error":True, "message": f"method {request.method} is not allowed."})

@items_api.route('/item/getPrefixes', methods=["GET"])
@login_required
def getModalPrefixes():
    """ GET prefixes from the system by passing page and limit.
    ---
    parameters:
        - in: query
          name: page
          schema:
            type: integer
            minimum: 1
            default: 1
          description: page of the database records
        - in: query
          name: limit
          schema:
            type: integer
            minimum: 1
            default: 10
          description: number of database records to GET
    responses:
        200:
            description: Prefixes received from the system successfully!
    """
    recordset = []
    count = 0
    if request.method == "GET":
        page = int(request.args.get('page', 1))
        limit = int(request.args.get('limit', 10))
        site_name = session['selected_site']
        offset = (page - 1) * limit
        recordset, count = database_items.getPrefixes(site_name, (limit, offset))
        print(recordset, count)
        return jsonify({"prefixes":recordset, "end":math.ceil(count/limit), "error":False, "message":"items fetched succesfully!"})
    return jsonify({"prefixes":recordset, "end":math.ceil(count/limit), "error":True, "message":f"method {request.method} is not allowed!"})

@items_api.route('/item/getZonesBySku', methods=["GET"])
@login_required
def getZonesbySku():
    """ GET zones by sku by passing page, limit, item_id
    ---
    parameters:
        - in: query
          name: page
          schema:
            type: integer
            minimum: 1
            default: 1
          description: page of the records to GET
        - in: query
          name: limit
          schema:
            type: integer
            minimum: 1
            default: 10
          description: number of records to grab from the system
        - in: query
          name: item_id
          schema:
            type: integer
            minimum: 1
            default: 1
          description: item_id to pull zones for
    responses:
        200:
            description: Zones received successfully.
    """
    zones, count = [], 0
    if request.method == "GET":
        page = int(request.args.get('page', 1))
        limit = int(request.args.get('limit', 10))
        item_id = int(request.args.get('item_id'))
        site_name = session['selected_site']
        offset = (page - 1) * limit
        zones, count = database_items.paginateZonesBySku(site_name, (item_id, limit, offset))
        return jsonify({'zones': zones, 'endpage': math.ceil(count/limit), 'error':False, 'message': f''})
    return jsonify({'zones': zones, 'endpage': math.ceil(count/limit), 'error':False, 'message': f'method {request.method} not allowed.'})

@items_api.route('/item/getLocationsBySkuZone', methods=['GET'])
@login_required
def getLocationsBySkuZone():
    """ GET locations by sku by passing page, limit, item_id, zone_id
    ---
    parameters:
        - in: query
          name: page
          schema:
            type: integer
            minimum: 1
            default: 1
          description: page of the records to GET
        - in: query
          name: limit
          schema:
            type: integer
            minimum: 1
            default: 10
          description: number of records to grab from the system
        - in: query
          name: item_id
          schema:
            type: integer
            minimum: 1
            default: 1
          description: item_id to pull locations for zone_id
        - in: query
          name: zone_id
          schema:
            type: integer
            minimum: 1
            default: 1
          description: zone_id to pull locations for item_id
    responses:
        200:
            description: Locations received successfully.
    """
    locations, count = [], 0
    if request.method == "GET":
        zone_id = int(request.args.get('zone_id', 1))
        part_id = int(request.args.get('part_id', 1))
        page = int(request.args.get('page', 1))
        limit = int(request.args.get('limit', 1))
        offset = (page-1)*limit
        site_name = session['selected_site']
        locations, count = database_items.paginateLocationsBySkuZone(site_name, (part_id, zone_id, limit, offset))
        return jsonify({'locations': locations, 'endpage': math.ceil(count/limit), 'error': False, 'message': f''})
    return jsonify({'locations': locations, 'endpage': math.ceil(count/limit), 'error': True, 'message': f'method {request.method} is not allowed.'})

@items_api.route('/item/getBrands', methods=['GET'])
@login_required
def getBrands():
    """ GET brands from the system by passing page, limit
    ---
    parameters:
        - in: query
          name: page
          schema:
            type: integer
            minimum: 1
            default: 1
          description: page of the records to GET
        - in: query
          name: limit
          schema:
            type: integer
            minimum: 1
            default: 10
          description: number of records to grab from the system
    responses:
        200:
            description: Brands received successfully.
    """
    brands, count = [], 0
    if request.method == "GET":
        page = int(request.args.get('page', 1))
        limit = int(request.args.get('limit', 1))
        offset = (page-1)*limit
        site_name = session['selected_site']
        brands, count = database_items.paginateBrands(site_name, (limit, offset))
        return jsonify({'brands': brands, 'endpage': math.ceil(count/limit), 'error': False, 'message': f''})
    return jsonify({'brands': brands, 'endpage': math.ceil(count/limit), 'error': True, 'message': f'method {request.method} is not allowed.'})


@items_api.route('/item/updateItem', methods=['POST'])
@login_required
def updateItem():
    """ POST update to item in the system by passing item_id, data
    ---
    parameters:
        - in: query
          name: item_id
          schema:
            type: integer
            minimum: 1
            default: 1
          description: item_id that the POST targets
        - in: header
          name: data
          description: data to update in system
    responses:
        200:
            description: item updated successfully.
    """
    if request.method == "POST":
        id = request.get_json()['id']
        data = request.get_json()['data']
        site_name = session['selected_site']
        database_items.postUpdateItem(site_name, {'id': id, 'update': data, 'user_id': session['user_id']})
        return jsonify({'error': False, 'message': f'Item was updated successfully!'})
    return jsonify({'error': True, 'message': f'method {request.method} is not allowed!'})

@items_api.route('/item/updateItemLink', methods=['POST'])
@login_required
def updateItemLink():
    """ UPDATE item link by passing id, conv_factor, barcode, old_conv
    ---
    parameters:
        - in: query
          name: id
          schema:
            type: integer
            minimum: 1
            default: 1
          required: true
          description: Id of item link to update
        - in: query
          name: conv_factor
          schema:
            type: integer
          required: true
          description: new conversion factor of item_link id
        - in: query
          name: barcode
          schema:
            type: string
          required: true
          description: barcode of item_link id
        - in: query
          name: old_conv
          schema:
            type: integer
          required: true
          description: old conversion factor of item_link id
    responses:
        200:
            description: Item Link updated successfully.
    """
    if request.method == "POST":
        id = request.get_json()['id']
        conv_factor = request.get_json()['conv_factor']
        barcode = request.get_json()['barcode']
        old_conv_factor = request.get_json()['old_conv']
        site_name = session['selected_site']
        payload = {'id': id, 'update':{'conv_factor': conv_factor}, 'barcode': barcode, 'old_conv_factor': old_conv_factor, 'user_id':session['user_id'] }
        database_items.postUpdateItemLink(site_name, payload)
        return jsonify({'error':False, 'message': "Linked Item was updated successfully"})
    return jsonify({'error': True, 'message': f"method {request.method} not allowed."})


@items_api.route('/item/getPossibleLocations', methods=["GET"])
@login_required
def getPossibleLocations():
    """ GET locations with zones by passing a page and limit
    ---
    parameters:
        - in: query
          name: page
          schema:
            type: interger
            minimum: 1
            default: 1
          description: page in the records to GET
        - in: query
          name: limit
          schema:
            type: interger
            minimum: 1
            default: 1
          description: number of records to GET
    responses:
        200:
            description: Locations GET successful.
    """
    locations, count = (), 0
    if request.method == "GET":
        page = int(request.args.get('page', 1))
        limit = int(request.args.get('limit', 1))
        offset = (page-1)*limit
        site_name = session['selected_site']
        locations, count = database_items.paginateLocationsWithZone(site_name, (limit, offset))
        return jsonify({'locations': locations, 'end':math.ceil(count/limit), 'error':False, 'message': f'Locations received successfully!'})
    return jsonify({'locations': locations, 'end':math.ceil(count/limit), 'error':True, 'message': f'method {request.method} not allowed.'})

@items_api.route('/item/getLinkedItem', methods=["GET"])
@login_required
def getLinkedItem():
    """ GET itemlink from system by passing an ID
    ---
    parameters:
        - in: query
          name: id
          schema:
            type: integer
            default: 1
          required: true
          description: item link to get from the system
    responses:
        200:
            description: Item Link GET successful.
    """
    linked_item = {}
    if request.method == "GET":
        id = int(request.args.get('id', 1))
        site_name = session['selected_site']
        linked_item = database_items.getItemLink(site_name, (id, ))
        return jsonify({'linked_item': linked_item, 'error': False, 'message': 'Linked Item added!!'})
    return jsonify({'linked_item': linked_item, 'error': True, 'message': f'method {request.method} not allowed'})

@items_api.route('/item/addLinkedItem', methods=["POST"])
@login_required 
def addLinkedItem():
    """ POST a link between items by passing a parent_id, a child_id, conv_factor
    ---
    parameters:
        - in: query
          name: parent_id
          schema:
            type: integer
            default: 1
          required: true
          description: id to linked list item
        - in: query
          name: child_id
          schema:
            type: integer
            default: 1
          required: true
          description: id to item to be linked to list.
        - in: query
          name: conv_factor
          schema:
            type: integer
            default: 1
          required: true
          description: integer factor between child id to parent id.
    responses:
        200:
            description: Items linked successfully.
    """
    if request.method == "POST":
        parent_id = request.get_json()['parent_id']
        child_id = request.get_json()['child_id']
        conv_factor = request.get_json()['conv_factor']
        site_name = session['selected_site']
        user_id = session['user_id']
        
        items_processes.postLinkedItem(site_name, {
            'parent_id': parent_id,
            'child_id': child_id,
            'user_id': user_id,
            'conv_factor': conv_factor
        })
            
        return jsonify({'error': False, 'message': 'Linked Item added!!'})
    return jsonify({'error': True, 'message': 'These was an error with adding to the linked list!'})
    
@items_api.route('/items/addBlankItem', methods=["POST"])
def addBlankItem():
    if request.method == "POST":
        data = {
            'barcode': request.get_json()['barcode'], 
            'name': request.get_json()['name'], 
            'subtype': request.get_json()['subtype']
            }
        pprint.pprint(data)
        database_config = config()
        site_name = session['selected_site']
        user_id = session['user_id']
        try:
            with psycopg2.connect(**database_config) as conn:
                process.postNewBlankItem(conn, site_name, user_id, data)
        except Exception as error:
            conn.rollback()
            return jsonify({'error': True, 'message': error})
        return jsonify({'error': False, 'message': 'Item added!!'})
    return jsonify({'error': True, 'message': 'These was an error with adding Item!'})

@items_api.route('/items/addSKUPrefix', methods=["POST"])
def addSKUPrefix():
    if request.method == "POST":
        database_config = config()
        site_name = session['selected_site']
        try:
            with psycopg2.connect(**database_config) as conn:
                prefix = db.SKUPrefixTable.Payload(
                    request.get_json()['uuid'],
                    request.get_json()['name'], 
                    request.get_json()['description']
                )
                db.SKUPrefixTable.insert_tuple(conn, site_name, prefix.payload())
        except Exception as error:
            conn.rollback()
            return jsonify({'error': True, 'message': error})
        return jsonify({'error': False, 'message': 'Prefix added!!'})
    return jsonify({'error': True, 'message': 'These was an error with adding this Prefix!'})

@items_api.route('/item/addConversion', methods=['POST'])
def addConversion():
    if request.method == "POST":
        item_id = request.get_json()['parent_id']
        uom_id = request.get_json()['uom_id']
        conv_factor = request.get_json()['conv_factor']

        database_config = config()
        site_name = session['selected_site']
        with psycopg2.connect(**database_config) as conn:
            conversion = db.ConversionsTable.Payload(
                item_id, uom_id, conv_factor
            )
            db.ConversionsTable.insert_tuple(conn, site_name, conversion.payload())
            
            return jsonify(error=False, message="Conversion was added successfully")
    return jsonify(error=True, message="Unable to save this conversion, ERROR!")

@items_api.route('/item/deleteConversion', methods=['POST'])
def deleteConversion():
    if request.method == "POST":
        conversion_id = request.get_json()['conversion_id']
        print(conversion_id)
        database_config = config()
        site_name = session['selected_site']
        with psycopg2.connect(**database_config) as conn:
            db.ConversionsTable.delete_item_tuple(conn, site_name, (conversion_id,))
            
            return jsonify(error=False, message="Conversion was deleted successfully")
    return jsonify(error=True, message="Unable to delete this conversion, ERROR!")

@items_api.route('/item/updateConversion', methods=['POST'])
def updateConversion():
    if request.method == "POST":
        conversion_id = request.get_json()['conversion_id']
        update_dictionary = request.get_json()['update']

        database_config = config()
        site_name = session['selected_site']
        with psycopg2.connect(**database_config) as conn:
            db.ConversionsTable.update_item_tuple(conn, site_name, {'id': conversion_id, 'update': update_dictionary})
            return jsonify(error=False, message="Conversion was updated successfully")
    return jsonify(error=True, message="Unable to save this conversion, ERROR!")

@items_api.route('/item/addPrefix', methods=['POST'])
def addPrefix():
    if request.method == "POST":
        item_info_id = request.get_json()['parent_id']
        prefix_id = request.get_json()['prefix_id']
        print(item_info_id)
        print(prefix_id)
        database_config = config()
        site_name = session['selected_site']
        with psycopg2.connect(**database_config) as conn:
            prefixes = db.ItemInfoTable.select_tuple(conn, site_name, (item_info_id,))['prefixes']
            print(prefixes)
            prefixes.append(prefix_id)
            db.ItemInfoTable.update_tuple(conn, site_name, {'id': item_info_id, 'update':{'prefixes': prefixes}})
            return jsonify(error=False, message="Prefix was added successfully")
    return jsonify(error=True, message="Unable to save this prefix, ERROR!")

@items_api.route('/item/deletePrefix', methods=['POST'])
def deletePrefix():
    if request.method == "POST":
        item_info_id = request.get_json()['item_info_id']
        prefix_id = request.get_json()['prefix_id']

        database_config = config()
        site_name = session['selected_site']
        with psycopg2.connect(**database_config) as conn:
            prefixes = db.ItemInfoTable.select_tuple(conn, site_name, (item_info_id,))['prefixes']
            prefixes.remove(prefix_id)
            db.ItemInfoTable.update_tuple(conn, site_name, {'id': item_info_id, 'update':{'prefixes': prefixes}})
            return jsonify(error=False, message="Prefix was deleted successfully")
    return jsonify(error=True, message="Unable to delete this prefix, ERROR!")

@items_api.route('/item/refreshSearchString', methods=['POST'])
def refreshSearchString():
    if request.method == "POST":
        item_id = request.get_json()['item_id']

        database_config = config()
        site_name = session['selected_site']
        with psycopg2.connect(**database_config) as conn:
            item = db.ItemTable.getItemAllByID(conn, site_name, (item_id,))
            parameters = [f"id::{item['id']}", f"barcode::{item['barcode']}", f"name::{item['item_name']}", f"brand::{item['brand']['name']}", 
                          f"expires::{item['food_info']['expires']}", f"row_type::{item['row_type']}", f"item_type::{item['item_type']}"]
            
            for prefix in item['item_info']['prefixes']:
                parameters.append(f"prefix::{prefix['name']}")

            search_string = "&&".join(parameters)
            db.ItemTable.update_tuple(conn, site_name, {'id': item_id, 'update':{'search_string': search_string}})

            return jsonify(error=False, message="Search String was updated successfully")
    return jsonify(error=True, message="Unable to update this search string, ERROR!")

@items_api.route('/item/postNewItemLocation', methods=['POST'])
def postNewItemLocation():
    if request.method == "POST":
        item_id = request.get_json()['item_id']
        location_id = request.get_json()['location_id']
        database_config = config()
        site_name = session['selected_site']
        with psycopg2.connect(**database_config) as conn:
            item_location = db.ItemLocationsTable.Payload(
                item_id,
                location_id
            )
            db.ItemLocationsTable.insert_tuple(conn, site_name, item_location.payload())
            return jsonify(error=False, message="Location was added successfully")
    return jsonify(error=True, message="Unable to save this location, ERROR!")