from odoo import api, exceptions, fields, models, _
import datetime


class IRNControl(models.Model):
    _name = "irn.control"
    _description = "IRN Control"
    _order = 'id desc'

    scan = fields.Char('PO Scan')
    product_scan = fields.Char('Product Scan')
    warehouse_id = fields.Many2one('stock.warehouse', string='Warehouse Location', required=True)
    irn_number = fields.Char('IRN Number')
    po_number = fields.Char('PO Number')
    po_id = fields.Integer('Purchase ID')
    remark = fields.Text('Remark')
    received_by = fields.Many2one('res.users', 'Received By', default=lambda self: self.env.uid)
    qc_pushed = fields.Boolean('QC Pushed')
    date_confirm = fields.Datetime('Confirmation Date', select=False)
    state = fields.Selection([
        ('full_received', 'Full Received'),
        ('partial_received', 'Partial Received'),

    ], 'Status', copy=False, help="Gives the status of the IRN control", select=True)
    irn_line = fields.One2many('irn.control.line', 'irn_line_id', 'IRN Line', required=True)

    def confirm_wms_inbound_irn(self):

        # check if already irn pushed
        if not self.qc_pushed:
            qc_data = {
                'irn_number': str(self.irn_number),
                'irn_id': self.id,
                'warehouse_id': self.warehouse_id.id,
                'po_number': str(self.po_number),
                'po_id': self.po_id,
                'remark': str(self.remark),
                'received_by': self.received_by.id
            }

            qc_line_list = list()

            for line_item in self.irn_line:
                qc_line_list.append([0, False,{
                     'product': line_item.product,
                     'product_id': line_item.product_id,
                     'product_ean': line_item.product_ean,
                     'description': line_item.remark,
                     'purchase_quantity': line_item.ordered_quantity,
                     'received_quantity': line_item.received_quantity,
                     'accepted_quantity': 0,
                     'pending_quantity': line_item.received_quantity,
                     }])

            qc_data['qc_line'] = qc_line_list

            qc_env = self.env['quality.control']
            saved_qc_id = qc_env.create(qc_data)

            for single_id in self:
                qc_pushed_update_query = "UPDATE irn_control SET qc_pushed=TRUE, date_confirm='{0}' " \
                                         "WHERE id={1}".format(str(fields.datetime.now()), single_id.id)
                self._cr.execute(qc_pushed_update_query)
                self._cr.commit()

                qc_pushed_update_query_irn_line = "UPDATE irn_control_line SET qc_pushed=TRUE WHERE " \
                                                  "irn_line_id={0}".format(single_id.id)
                self._cr.execute(qc_pushed_update_query_irn_line)
                self._cr.commit()

            # # update irn end-to-end
            # self.env['inb.end.to.end'].irn_confirm_update(str(self.po_number),
            #                                                    self.po_id, str(fields.datetime.now()))

        return True

    def _check_if_already_irned(self, po_number):

        # need to check on the po already IRNed
        irn_state = 'new'

        irn_obj = self.search([('po_number', '=', po_number)])

        for irn in irn_obj:
            if str(irn.state) == 'full_received':
                irn_state = 'full_received'
                break
            elif str(irn.state) == 'partial_received':
                irn_state = 'partial_received'
            else:
                irn_state = 'new'

        return irn_state

    def _po_confirmation(self, po):

        po_env = self.env['purchase.order']
        po_obj = po_env.search([('name', '=', po)])
        po_state = str(po_obj.state)
        return po_state

    def _check_full_or_partial_received(self, record, po_number):
        full_or_partial_rec = False

        for line_item in record.irn_line:

            line_received_qty = self._check_irn_line_received_quantity(po_number, line_item.product_id)

            if line_item.ordered_quantity != line_received_qty:
                full_or_partial_rec = False
                break
            else:
                full_or_partial_rec = True

        return full_or_partial_rec

    # -------------------------------------------------------------
    def _process_partial_received_order(self, po_number):

        partial_received_product_id_list = list()
        partial_rec_prod_list = list()

        irn_obj = self.search([('po_number', '=', po_number), ('state', '=', 'partial_received')])

        for irn in irn_obj:
            for product in irn.irn_line:
                if product.state == 'partially_received' and product.product_id not in partial_received_product_id_list:
                    partial_received_product_id_list.append(product.product_id)

        for pr_product_id in partial_received_product_id_list:
            pr_product = self._process_partial_received_product(po_number, pr_product_id)
            if len(pr_product.keys()) > 0:
                partial_rec_prod_list.append(pr_product)

        # return list of dictionary
        return partial_rec_prod_list

    def _process_partial_received_product(self, po_number, product_id):

        irn_obj = self.search([('po_number', '=', po_number)])
        irn_line_env = self.env['irn.control.line']
        product_received_quantity = self._check_irn_line_received_quantity(po_number, product_id)
        product_name = ''
        ordered_quantity = 0

        ###########################################
        for irn in irn_obj:
            irn_line_obj = irn_line_env.search([('irn_line_id', '=', irn.id),('product_id', '=', product_id)])
            for product in irn_line_obj:
                product_name = product.product
                product_id = product_id
                ordered_quantity = product.ordered_quantity
        ###########################################

        if ordered_quantity > product_received_quantity:

            product_env = self.env['product.product']
            product_obj = product_env.search([('id', '=', product_id)])

            product_dict = {
                    'product': product_name,
                    'product_id': product_id,
                    'product_ean': str(product_obj.ean13) if product_obj.ean13 else '',
                    'ordered_quantity': ordered_quantity,
                    'received_quantity': ordered_quantity - product_received_quantity,
                    'remark': ''
                }
            return product_dict
        else:
            return {}
    # -------------------------------------------------------------

    @api.onchange('scan')
    def po_irn_barcode_onchange(self):


        try:

            po_number = str(self.scan)

            self.po_number = po_number
            po_env = self.env['purchase.order']
            po_obj = po_env.search([('name', '=', po_number)])

            po_line_env = self.env['purchase.order.line']
            po_line_obj = po_line_env.search([('order_id', '=', po_obj.id)])

            self.warehouse_id = po_obj.picking_type_id.warehouse_id.id

            po_line_list = list()


            for po_line in po_line_obj:
                item = {

                    'product': str(po_line.name),
                    'product_id': int(po_line.product_id.id),
                    'product_ean': str(po_line.product_id.barcode) if po_line.product_id.barcode else '',
                    'ordered_quantity': str(po_line.product_qty),
                    'received_quantity': 0,
                    'remark': ''
                }
                po_line_list.append((0, 0, item))

            self.irn_line = po_line_list


            self.scan = ""
            # if po_number:
            #
            #     irn_state = self._check_if_already_irned(po_number)
            #     po_state = self._po_confirmation(po_number)
            #
            #     # if po_state == 'cancel' or po_state == 'draft':
            #     if po_state in ['cancel', 'draft', 'bid', 'category_head_approval_pending',
            #                     'category_vp_approval_pending', 'coo_approval_pending', 'ceo_approval_pending']:
            #         raise exceptions.ValidationError(_('PO is not confirmed.\n This PO is in '
            #                                            'Draft/Approval Pending/Cancel mode'))
            #     else:
            #
            #         if irn_state == 'full_received':
            #             # raise exception
            #             raise exceptions.ValidationError(_('Already IRN done!! \n IRN already done for this PO!!!'))
            #
            #         elif irn_state == 'partial_received':
            #             # load only left products
            #             # search in irn_control with po_number
            #             self.irn_line = self._process_partial_received_order(po_number)
            #             self.po_number = po_number
            #             po_env = self.env['purchase.order']
            #             po_obj = po_env.search([('name', '=', po_number)])
            #             self.warehouse_id = po_obj.picking_type_id.warehouse_id.id
            #             self.scan = ""
            #
            #         else:
            #             self.po_number = po_number
            #             po_env = self.env['purchase.order']
            #             po_obj = po_env.search([('name', '=', po_number)])
            #
            #             po_line_env = self.env['purchase.order.line']
            #             po_line_obj = po_line_env.search([('order_id', '=', po_obj.id)])
            #
            #             self.warehouse_id = po_obj.picking_type_id.warehouse_id.id
            #
            #             po_line_list = list()
            #
            #             for po_line in po_line_obj:
            #                 item = {
            #                     'product': str(po_line.name),
            #                     'product_id': int(po_line.product_id.id),
            #                     'product_ean': str(po_line.product_id.barcode) if po_line.product_id.barcode else '',
            #                     'ordered_quantity': str(po_line.product_qty),
            #                     'received_quantity': 0,
            #                     'remark': ''
            #                 }
            #                 po_line_list.append((0, 0, item))
            #
            #             self.irn_line = po_line_list
            #
            #             self.scan = ""
        except:

            self.scan = ""

    def _check_irn_line_received_quantity(self, po_number, product_id):

        received_quantity = 0

        irn_env = self.env['irn.control']
        irn_obj = irn_env.search([('po_number', '=', po_number)])

        irn_line_env = self.env['irn.control.line']

        for irn in irn_obj:
            irn_line_obj = irn_line_env.search([('irn_line_id', '=', irn.id), ('product_id', '=', product_id)])
            for irn_line in irn_line_obj:
                received_quantity += irn_line.received_quantity

        return received_quantity

    def _set_irn_line_state(self, record, po_number):

        for line_item in record.irn_line:
            line_received_qty = self._check_irn_line_received_quantity(po_number, line_item.product_id)
            if line_item.ordered_quantity == line_received_qty:
                line_item.state = 'fully_received'
            else:
                line_item.state = 'partially_received'

        return record

    def _set_po_id_in_line(self, record, po_id):

        for line_item in record.irn_line:
            line_item.po_id = po_id

        return record

    @api.model
    def create(self, vals):
        # generate IRN number
        # IRN0123456


        record = super(IRNControl, self).create(vals)


        po_env = self.env['purchase.order']
        po_obj = po_env.search([('name', '=', str(record.po_number))])

        irn_number = "IRN0"+str(record.id)
        po_id = po_obj.id

        record.irn_number = irn_number
        record.po_id = po_id
        record.state = 'full_received' if self._check_full_or_partial_received(record, str(record.po_number)) else 'partial_received'

        record = self._set_irn_line_state(record, str(record.po_number))
        record = self._set_po_id_in_line(record, po_id)

        # ---------------------------
        create_permission = list()

        if 'irn_line' in vals :
            for single_line in vals['irn_line']:

                if not not single_line[2]:

                    if single_line[2]['received_quantity'] > single_line[2]['ordered_quantity']:
                        create_permission.append(False)
                    else:
                        create_permission.append(True)

        # if False in create_permission:
        #
        #     raise exceptions.ValidationError(_('IRN line adjustment ERROR!!!\n Received quantity should be less then'
        #                                        ' or equal to (ordered quantity)!!!'))
        #
        # else:
        #     self.env['inb.end.to.end'].irn_data_create(record.po_number, po_id, record.id,irn_number,record.create_date)
            # ---------------------------
        return record

    # calls at the time of update record
    def write(self, vals):
        # only update only if (received_quantity less or equal to ordered_quantity)
        update_permission = list()

        if 'irn_line' in vals :
            for single_line in vals['irn_line']:
                if not not single_line[2]:
                    for irn_l in self.irn_line:
                        if irn_l.id == single_line[1]:

                            received_quantity = single_line[2]['received_quantity'] \
                                if 'received_quantity' in single_line[2] else irn_l.received_quantity
                            if received_quantity < irn_l.ordered_quantity:
                                vals['state']='partial_received'
                                single_line[2]['state'] = 'partially_received'
                            else:
                                vals['state'] = 'full_received'
                                single_line[2]['state'] = 'fully_received'

                            if received_quantity > irn_l.ordered_quantity:
                                update_permission.append(False)
                            else:
                                update_permission.append(True)

        if False in update_permission:
            raise exceptions.ValidationError(_('IRN line adjustment ERROR!!! \n Received quantity '
                                               'should be less then or equal to (ordered quantity)!!!'))

        record = super(IRNControl, self).write(vals)

        return record

    @api.onchange('product_scan')
    def po_irn_product_scan(self):
        try:
            po_number = str(self.po_number)

            product_scan = str(self.product_scan)
            product_not_in_po_order = True

            if product_scan != 'False':
                qty = 1
                if 'Prod' not in product_scan:

                    product_env = self.env['product.product']
                    product_obj = product_env.search(
                        ['|', '|', '|', '|', ('ean13', '=', str(product_scan)), ('gift_ean', '=', str(product_scan)),
                         ('box_ean', '=', str(product_scan)), ('case_ean', '=', str(product_scan)),
                         ('pallet_ean', '=', str(product_scan))])

                    if not product_obj.id:
                        raise exceptions.ValidationError(_(
                            'Product Not found.Please add EAN number in Product Configuration. Or contact with Your department head'))

                    if str(product_obj.gift_ean) == str(product_scan):
                        qty = 1
                    elif str(product_obj.box_ean) == str(product_scan):
                        qty = product_obj.holding_box_qty
                    elif str(product_obj.case_ean) == str(product_scan):
                        qty = product_obj.holding_case_qty
                    elif str(product_obj.pallet_ean) == str(product_scan):
                        qty = product_obj.holding_pallet_qty
                else:
                    get_list = product_scan.split('Prod')
                    product_id = int(get_list[1])

                    product_env = self.env['product.product']
                    product_obj = product_env.search([('id', '=', product_id)])
                    if not product_obj.id:
                        raise exceptions.ValidationError(_('Product Not found.Please scan properly with proper product'))

                po_env = self.env['purchase.order']
                po_obj = po_env.search([('name', '=', po_number)])

                po_line_env = self.env['purchase.order.line']
                po_line_obj = po_line_env.search([('order_id', '=', po_obj.id)])

                # po_line_obj[0].product_id.ean13
                # self.irn_line[0].product_ean
                for single_line in self.irn_line:
                    if str(single_line.product_id) == str(product_obj.id) and single_line.ordered_quantity >= (single_line.received_quantity +qty):
                        single_line.received_quantity += qty
                        product_not_in_po_order=False
                        break
                # self.irn_line[0].received_quantity

                if product_not_in_po_order:
                    raise exceptions.ValidationError(_('Product Not found in this Order.Wrong product has been scanned'))


                self.product_scan = ""

        except:
            self.product_scan = ""


class IRNControlLine(models.Model):
    _name = "irn.control.line"
    _description = "IRN Control Line"

    irn_line_id = fields.Many2one('irn.control', 'IRN Line ID', required=True,
                                  ondelete='cascade', select=True, readonly=True)
    po_id = fields.Many2one('purchase.order', 'PO ID', required=False,
                             ondelete='cascade', select=True, readonly=True)
    product = fields.Char('Product')
    product_id = fields.Integer('Product ID')
    product_ean = fields.Char('Product EAN')
    ordered_quantity = fields.Float('Ordered Qty')
    received_quantity = fields.Float('Received Qty')
    remark = fields.Char('Remark')
    qc_pushed = fields.Boolean('QC Pushed')
    state = fields.Selection([
            ('fully_received', 'Fully Received'),
            ('partially_received', 'Partially Received'),
            ('cancel', 'Cancelled'),
        ], 'Status', help="Gives the status of the IRN control line", select=True)


class IRNWithEan(models.Model):
    _name = "irn.with.ean"
    _description = "IRN With EAN"

    product = fields.Char('Product')
    product_id = fields.Integer('Product ID')
    product_ean = fields.Char('EAN')

    def save_irn_with_product_ean(self):
        context = self.env.context
        product_id = context['product_id']
        product_ean = context['product_ean']

        if len(product_ean) > 13 or " " in product_ean:
            # show error. not a valid ean
            raise exceptions.ValidationError(_('Not a valid EAN number!!!'))
        else:

            ean_update_query = "UPDATE product_product SET ean13='{0}' WHERE id='{1}'".format(product_ean, product_id)
            self._cr.execute(ean_update_query)
            self._cr.commit()

            irn_line_query = "UPDATE irn_control_line SET product_ean='{0}' WHERE product_id='{1}'".format\
                (product_ean, product_id)
            self._cr.execute(irn_line_query)
            self._cr.commit()

        # return True
        return {
            'type': 'ir.actions.client',
            'tag': 'reload',
        }

    def cancel_irn_with_product_ean(self):
        return {
            'type': 'ir.actions.client',
            'tag': 'reload',
        }
