# -*- coding: utf-8 -*-
import logging
from unittest.mock import patch

import odoo
from odoo.addons.point_of_sale.models.pos_order import PosOrder
from odoo.addons.point_of_sale.models.pos_session import PosSession
from odoo.addons.point_of_sale.tests.common import TestPoSCommon


class IntendedException(Exception):
    pass


def mocked_process_order(*args):
    # We just want the order process to crash (to see if it is captured)
    raise IntendedException()


def mocked_handle_order_process_fail(self, order: dict, exception: Exception, draft: bool):
    # We DO NOT want to create a new env in the test as the current pos_session does not exist (as it was not committed)
    self._process_order_process_fail(order, exception)


@odoo.tests.tagged('post_install', '-at_install')
class TestPosCapture(TestPoSCommon):
    """
    Test the capture system of failed to process orders
    """

    def setUp(self):
        super(TestPosCapture, self).setUp()
        self.config = self.basic_config

        self.product1 = self.create_product('Product 1', self.categ_basic, 10, 5)

    def assert_activity_and_attachment(self, pos_session, number):
        pos_attachments_domain = [
            ['res_model', '=', pos_session._name],
            ['res_id', '=', pos_session.id]
        ]
        self.assertEqual(len(pos_session.activity_ids), number)
        self.assertEqual(len(self.env['ir.attachment'].search(pos_attachments_domain)), number)

    def test_capture_one_order(self):
        # open a session
        session = self.open_new_session()

        orders = [self.create_ui_order_data([(self.product1, 1)])]

        self.assert_activity_and_attachment(session, 0)
        with patch.object(PosOrder, '_process_order', mocked_process_order),\
            patch.object(PosSession, '_handle_order_process_fail', mocked_handle_order_process_fail),\
            self.assertLogs('odoo.addons.point_of_sale.models.pos_order', level=logging.ERROR) as logger_error_output:
            try:
                self.env['pos.order'].create_from_ui(orders)
            except IntendedException:
                self.assertIn("An error occurred when processing the PoS order", logger_error_output.output[0])
                self.assert_activity_and_attachment(session, 1)
                self.assertEqual(session.activity_ids[0].user_id.id, self.env.user.id)

    def test_capture_two_orders(self):
        """Two order even with same content should have distinct captured file"""
        # open a session
        session = self.open_new_session()

        order1 = [self.create_ui_order_data([(self.product1, 1)], uid='12345-678-1996')]
        order2 = [self.create_ui_order_data([(self.product1, 1)], uid='12345-678-1999')]  # Different order with same content but different uuid

        with patch.object(PosOrder, '_process_order', mocked_process_order),\
            patch.object(PosSession, '_handle_order_process_fail', mocked_handle_order_process_fail),\
            self.assertLogs('odoo.addons.point_of_sale.models.pos_order', level=logging.ERROR):
            try:
                self.env['pos.order'].create_from_ui(order1)
            except IntendedException:
                self.assert_activity_and_attachment(session, 1)

            try:
                self.env['pos.order'].create_from_ui(order2)
            except IntendedException:
                self.assert_activity_and_attachment(session, 2)

    def test_capture_one_order_twice(self):
        """Should have only one attachment as we sync the same order twice"""
        # open a session
        session = self.open_new_session()

        orders = [self.create_ui_order_data([(self.product1, 1)])]

        self.assert_activity_and_attachment(session, 0)
        with patch.object(PosOrder, '_process_order', mocked_process_order),\
            patch.object(PosSession, '_handle_order_process_fail', mocked_handle_order_process_fail),\
            self.assertLogs('odoo.addons.point_of_sale.models.pos_order', level=logging.ERROR):
            for _ in range(2):
                try:
                    self.env['pos.order'].create_from_ui(orders)
                except IntendedException:
                    self.assert_activity_and_attachment(session, 1)

    def test_capture_order_same_uuid(self):
        """Should have 2 attachments as the content is different"""
        # open a session
        session = self.open_new_session()

        order1 = [self.create_ui_order_data([(self.product1, 1)], uid='12345-678-1996')]
        order2 = [self.create_ui_order_data([(self.product1, 2)], uid='12345-678-1996')]

        self.assert_activity_and_attachment(session, 0)
        with patch.object(PosOrder, '_process_order', mocked_process_order),\
            patch.object(PosSession, '_handle_order_process_fail', mocked_handle_order_process_fail),\
            self.assertLogs('odoo.addons.point_of_sale.models.pos_order', level=logging.ERROR):
            try:
                self.env['pos.order'].create_from_ui(order1)
            except IntendedException:
                self.assert_activity_and_attachment(session, 1)

            try:
                self.env['pos.order'].create_from_ui(order2)
            except IntendedException:
                self.assert_activity_and_attachment(session, 2)

    def test_capture_one_order_and_removed(self):
        """Check if the attachment and activity is automatically remove after the order sync"""
        # open a session
        session = self.open_new_session()

        orders = [self.create_ui_order_data([(self.product1, 1)])]

        self.assert_activity_and_attachment(session, 0)
        with patch.object(PosOrder, '_process_order', mocked_process_order),\
            patch.object(PosSession, '_handle_order_process_fail', mocked_handle_order_process_fail),\
            self.assertLogs('odoo.addons.point_of_sale.models.pos_order', level=logging.ERROR):
            try:
                self.env['pos.order'].create_from_ui(orders)
            except IntendedException:
                pass

        self.assert_activity_and_attachment(session, 1)
        # Resync the order, this time it should go through!
        self.env['pos.order'].create_from_ui(orders)
        # Should automatically remove the attachment for this order after sync
        self.assert_activity_and_attachment(session, 0)

    def test_capture_two_orders_and_removed(self):
        """Check if the attachment and activity is automatically remove after the order sync (with 2 orders)"""
        # open a session
        session = self.open_new_session()

        order1 = [self.create_ui_order_data([(self.product1, 1)], uid='12345-678-1996')]
        order2 = [self.create_ui_order_data([(self.product1, 1)], uid='12345-678-1999')]  # Different order with same content but different uuid

        with patch.object(PosOrder, '_process_order', mocked_process_order),\
            patch.object(PosSession, '_handle_order_process_fail', mocked_handle_order_process_fail),\
            self.assertLogs('odoo.addons.point_of_sale.models.pos_order', level=logging.ERROR):
            try:
                self.env['pos.order'].create_from_ui(order1)
            except IntendedException:
                self.assert_activity_and_attachment(session, 1)

            try:
                self.env['pos.order'].create_from_ui(order2)
            except IntendedException:
                self.assert_activity_and_attachment(session, 2)

        self.assert_activity_and_attachment(session, 2)
        # Resync the order, this time it should go through!
        self.env['pos.order'].create_from_ui(order2)
        # Should automatically remove the attachment for this order after sync
        self.assert_activity_and_attachment(session, 1)

        self.env['pos.order'].create_from_ui(order1)
        # Should automatically remove the attachment for this order after sync
        self.assert_activity_and_attachment(session, 0)
