from django.db import models

from outils.common_imports import *
from outils.common import OdooAPI
from orders.models import Order

import os, re

# Create your models here.

PO_TO_PROCESS_FILE_PREFIX = 'data/po_to_process'

class CagetteReception(models.Model):

    def __init__(self, id):
        """Init with odoo id."""
        self.id = int(id)
        self.o_api = OdooAPI()

    def get_orders(pids=[]):
        """
            Recupere la liste des BC en cours.
            pids: Id des purchase.order à récupérer. Limite la recherche si défini.
        """
        orders = []
        try:
            api = OdooAPI()
            if len(pids) == 0:
                f = ["purchase_id"]
                c = [['picking_type_id', '=', 1], ["state", "in", ['assigned', 'partially_available']]]
                res = api.search_read('stock.picking', c, f)
                pids = []
                if res and len(res) > 0:
                    for r in res:
                        pids.append(int(r['purchase_id'][0]))

            if len(pids):
                f=["id","name","date_order", "partner_id", "date_planned", "amount_untaxed", "amount_total", "reception_status", 'create_uid']

                # Only get orders that need to be treated in Reception
                c = [
                        ['id', 'in', pids],
                        ["reception_status", "in", [False, 'qty_valid', 'valid_pending', 'br_valid']],
                        ["state", "not in", ["cancel", "done"]]
                    ]

                orders = api.search_read('purchase.order', c, f)
        except Exception as e:
            coop_logger.error("CagetteReception.get_orders : %s", str(e))
        return orders

    def get_order_unprocessable_products(id_po):
        """Return all products that can't be processed in order"""
        return Order(id_po).get_inactive_products()

    def get_order_lines_by_po(id_po, nullQty=False):
        """Return all purchases order lines linked with purchase order id in Odoo."""
        return Order(id_po).get_lines(withNullQty=nullQty)
        
    def get_mail_create_po(id_po):
        """Return name et email from id_po of order"""
        try:
            api = OdooAPI()
            f = ["create_uid"] 
            c = [['id', '=', id_po]]
            
            res = api.search_read('purchase.order', c, f)

            f = ["email", "display_name"]
            c = [['user_ids', '=', int(res[0]['create_uid'][0])]]
            res = api.search_read('res.partner', c, f)


        except Exception as e:
            coop_logger.error("CagetteReception.get_mail_create_po : %s", str(e))
        
        return res

    def implies_scale_file_generation(self):
        answer = False
        lines_data = Order(self.id).get_lines()
        bc_pattern = re.compile('^0493|0499')  # TODO : Adjust for other pattern (such as Supercoop)
        for l in lines_data['lines']:
            if 'barcode' in l and not (bc_pattern.match(str(l['barcode'])) is None):
                #  "'barcode' in l" has been added after actual case where it was missing !
                answer = True
        # print ('answer=' + str(answer))
        return answer

    def update_line(self, idOnLine, updateType, pakageQty, nbPakage, uPrice):
        """Update purchase.order.line with qty data and/or price"""
        result = None
        try:
            f = {}

            if updateType == "qty_valid" or updateType == "br_valid":
                f['package_qty'] = pakageQty
                f['product_qty_package'] = nbPakage
                f['product_qty'] = pakageQty * nbPakage

            if updateType == "br_valid":
                f['price_unit'] = uPrice


            res = self.o_api.update('purchase.order.line', idOnLine, f)
            result = True
        except Exception as e:
            coop_logger.error("update_line : %s (fields values = %s, line_id = %s)",str(e), str(f), str(idOnLine))
            result = False
        return result

    def remove_package_restriction(self, order_line):
        """Set indicative_package to True"""
        f = {'indicative_package' : True}
        return self.o_api.update('product.supplierinfo', order_line['ps_info_id'], f)

    def update_order_status(self, id_po, updateType):
        """Update purchase.order with new reception status """
        f = {'reception_status':updateType}

        res = self.o_api.update('purchase.order', int(id_po), f)
        return res

    def attach_file(self, fileName, removeFile = True):
        """
        Attach file to purshase order.
        By default, remove entry file after operation.
        """
        return Order(self.id).attach_file(fileName, removeFile)


    def make_immediate_transfer(self, pack_operation_ids):
        """
            Pack operations have been created when order has been changed from draft to 'purchase'
            Qty have to be changed to fit with the actual received one
        """
        import json
        processed_lines = 0
        order_lines_data = CagetteReception.get_order_lines_by_po(self.id, nullQty=True)
        order_lines = order_lines_data['lines']
        received_products = {}
        for p in order_lines:
            received_products[p['product_id'][0]] = p['product_qty']
        packs = self.o_api.search_read('stock.pack.operation',[['id','in', pack_operation_ids]],['product_qty','product_id','linked_move_operation_ids'])
        if packs:
            for pack in packs:
                try:
                    if len(pack['linked_move_operation_ids']) == 1:
                        received_qty = 0
                        if pack['product_id'][0] in received_products:
                            received_qty = received_products[pack['product_id'][0]]
                            # First of all, change stock.move quantities to prevent missing quantities
                            pfields = {'product_qty_package': 1,
                                       'package_qty': received_qty,
                                      }
                            linked_move_op_id = int(pack['linked_move_operation_ids'][0])
                            mol_cond = [['id','=', linked_move_op_id]]
                            mol_f = ['move_id']
                            move_op_link = self.o_api.search_read('stock.move.operation.link', mol_cond, mol_f)
                            move_id = int(move_op_link[0]['move_id'][0])
                            self.o_api.update('stock.move.operation.link', [linked_move_op_id], {'qty': received_products[pack['product_id'][0]]})
                            pfields['product_uom_qty'] = received_qty
                            self.o_api.update('stock.move', [move_id], pfields)
                            del pfields['product_uom_qty']  # field not in stock.pack.operation
                            pfields['qty_done'] = pfields['product_qty'] = received_qty
                            self.o_api.update('stock.pack.operation', [int(pack['id'])], pfields)
                        processed_lines += 1
                    else:
                        # More than 1 move have been created : Attach a message to PO to advise
                        msg = 'Transfert non réalisé car le nombre de "linked_move_operation_ids" est > 1'
                        Order(self.id).attach_message(msg)
                except Exception as e:
                    print (str(e))
                    # link a message to PO to advise
                    msg = 'Transfert non réalisé : erreur sur produit ' + str(pack['product_id'])
                    msg += ' ' + str(e)
                    Order(self.id).attach_message(msg)

        return  processed_lines

    def print_shelf_labels_for_updated_prices(self, lines):
        import requests
        # don't print barcode which begin with these codes
        noprint_list = ["0493", "0492", "0499"]

        pids = []
        for l in lines:
            pids.append(l['product_id'][0])
        products_to_print = self.o_api.search_read('product.product', [['id','in', pids]], ['product_tmpl_id', 'barcode', 'to_print'])
        if products_to_print:
            to_reset = []
            for p_to_print in products_to_print:
                coop_logger.info('candidate to print %s', str(p_to_print))
                if p_to_print['barcode'] and p_to_print['to_print'] is True and p_to_print['barcode'][:4] not in noprint_list:
                    try:
                        tools_url = settings.TOOLS_SERVER + '/products/label_print/'
                        tools_url += str(p_to_print['product_tmpl_id'][0])
                        requests.get(tools_url)
                        to_reset.append(p_to_print['id'])
                    except Exception as e:
                        coop_logger.error("Shelf label printing : %s",str(e))
            if len(to_reset) > 0:
                self.o_api.update('product.product', to_reset, {'to_print': 0})
                    
    def update_products_price_v12(self):
        result = {'success': False}
        try:
            result['success'] = self.o_api.execute('purchase.order', 'update_po_price_to_vendor_price', [self.id])
        except Exception as e:
            coop_logger.error("update_products_price_v12 : %s", str(e))
            result['error'] = str(e)

        return result

    def stock_picking_update(self, order_line):
        result = None
        try:
            res = self.o_api.search_read('stock.move', [['purchase_line_id', '=', order_line['id']]], ['id'])
            if res:
                fields = {
                    'package_qty': float(order_line['package_qty']),
                    'product_qty_package': float(order_line['product_qty_package']),
                    'product_uom_qty': float(order_line['package_qty']) * float(order_line['product_qty_package']),
                    'price_unit': float(order_line['price_unit'])
                }
                ids = []
                for r in res:
                    ids.append(r['id'])
                self.o_api.update('stock.move', ids, fields)
                result = True

        except Exception as e:
            coop_logger.error("Stock picking update : %s ", str(e))
            result = False
        return result

    def finalyze_picking_v12(self):
        result = None
        try:
            res = self.o_api.execute('purchase.order', 'stock_immediate_transfer', [self.id])
            done = 0
            for r in res:
                if r in ['done', 'cancel']:
                    done += 1
            if done == len(res):
                result = 'processed'
                price_update = self.update_products_price_v12()
                if price_update['success'] is False:
                    result = 'error: price update'
                else:
                    if getattr(settings, 'RECEPTION_SHELF_LABEL_PRINT', False) is True:
                        self.print_shelf_labels_for_updated_prices(price_update['lines'])
            else:
                result = False

        except Exception as e:
            coop_logger.error("Finalyze picking : %s ", str(e))

        return result

    def register_purchase_order_to_finalyze(self):
        try:
            import datetime
            file = open(PO_TO_PROCESS_FILE_PREFIX + '_'+ str(self.id),'w')
            file.close()
            result = True
        except Exception as e:
            result = str(e)
        return result

    def process_enqueued_po_to_finalyze():
        to_process = []
        processed = []
        print_label = False
        for root, dirs, files in os.walk('data'):
            po_to_process_pattern = re.compile('po_to_process_([0-9]+)')
            po_in_process_pattern = re.compile('po_in_process_([0-9]+)')
            in_process_ids = []
            for basename in files:
                in_po_id = po_in_process_pattern.findall(basename)
                if len(in_po_id) > 0:
                    in_process_ids.append(in_po_id[0])
            for basename in files:
                id = po_to_process_pattern.findall(basename)
                if len(id) > 0 and not (id[0] in in_process_ids):
                    file = open('data/po_in_process_' + str(id[0]), 'w')
                    file.close()
                    to_process.append({'file': 'data/' + basename, 'id': str(id[0])})
        if len(to_process) > 0:
            for p in to_process:
                m = CagetteReception(int(p['id']))
                fp = m.finalyze_picking_v12()
                if fp == 'processed':
                    print_label = m.implies_scale_file_generation()
                if fp == 'processed' or fp == 'already done':
                    os.remove(p['file'])

                processed.append({p['id']: fp})
                os.remove('data/po_in_process_' + str(p['id']))
        if print_label is True:
            import requests
            requests.get(settings.TOOLS_SERVER + '/products/labels_appli_csv')
        return [to_process, processed]