# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from datetime import datetime
from dateutil.relativedelta import relativedelta
from unittest.mock import patch
from odoo.tests import tagged
from odoo.addons.base.tests.common import HttpCaseWithUserPortal
from odoo.addons.mail.models.mail_template import MailTemplate


class TestWebsiteSaleCartAbandonedCommon(HttpCaseWithUserPortal):

    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        now = datetime.utcnow()
        cls.customer = cls.env['res.partner'].create({
            'name': 'a',
            'email': 'a@example.com',
        })
        cls.public_partner = cls.env['res.partner'].create({
            'name': 'public',
            'email': 'public@example.com',
        })
        cls.public_user = cls.env['res.users'].create({
            'name': 'Foo', 'login': 'foo',
            'partner_id': cls.public_partner.id,
        })
        cls.website0 = cls.env['website'].create({
            'name': 'web0',
            'cart_abandoned_delay': 1.0,  # 1 hour
        })
        cls.website1 = cls.env['website'].create({
            'name': 'web1',
            'cart_abandoned_delay': 0.5,  # 30 minutes
        })
        cls.website2 = cls.env['website'].create({
            'name': 'web2',
            'cart_abandoned_delay': 24.0,  # 1 day
            'user_id': cls.public_user.id,  # specific public user
        })
        product = cls.env['product.product'].create({
            'name': 'The Product'
        })
        add_order_line = [[0, 0, {
            'name': 'The Product',
            'product_id': product.id,
            'product_uom_qty': 1,
        }]]
        cls.payment_method_id = cls.env.ref('payment.payment_method_unknown').id
        cls.so0before = cls.env['sale.order'].create({
            'partner_id': cls.customer.id,
            'website_id': cls.website0.id,
            'state': 'draft',
            'date_order': (now - relativedelta(hours=1)) - relativedelta(minutes=1),
            'order_line': add_order_line,
        })
        cls.so0after = cls.env['sale.order'].create({
            'partner_id': cls.customer.id,
            'website_id': cls.website0.id,
            'state': 'draft',
            'date_order': (now - relativedelta(hours=1)) + relativedelta(minutes=1),
            'order_line': add_order_line,
        })
        cls.so1before = cls.env['sale.order'].create({
            'partner_id': cls.customer.id,
            'website_id': cls.website1.id,
            'state': 'draft',
            'date_order': (now - relativedelta(minutes=30)) - relativedelta(minutes=1),
            'order_line': add_order_line,
        })
        cls.so1after = cls.env['sale.order'].create({
            'partner_id': cls.customer.id,
            'website_id': cls.website1.id,
            'state': 'draft',
            'date_order': (now - relativedelta(minutes=30)) + relativedelta(minutes=1),
            'order_line': add_order_line,
        })
        cls.so2before = cls.env['sale.order'].create({
            'partner_id': cls.customer.id,
            'website_id': cls.website2.id,
            'state': 'draft',
            'date_order': (now - relativedelta(hours=24)) - relativedelta(minutes=1),
            'order_line': add_order_line,
        })
        cls.so2after = cls.env['sale.order'].create({
            'partner_id': cls.customer.id,
            'website_id': cls.website2.id,
            'state': 'draft',
            'date_order': (now - relativedelta(hours=24)) + relativedelta(minutes=1),
            'order_line': add_order_line,
        })
        cls.so2before_but_public = cls.env['sale.order'].create({
            'partner_id': cls.public_partner.id,
            'website_id': cls.website2.id,
            'state': 'draft',
            'date_order': (now - relativedelta(hours=24)) - relativedelta(minutes=1),
            'order_line': add_order_line,
        })

        # Must behave like so1before because public partner is not the one of website1
        cls.so1before_but_other_public = cls.env['sale.order'].create({
            'partner_id': cls.public_partner.id,
            'website_id': cls.website1.id,
            'state': 'draft',
            'date_order': (now - relativedelta(minutes=30)) - relativedelta(minutes=1),
            'order_line': add_order_line,
        })

    def send_mail_patched(self, sale_order_id):
        email_got_sent = False

        def check_send_mail_called(this, res_id, email_values, *args, **kwargs):
            nonlocal email_got_sent
            if res_id == sale_order_id:
                email_got_sent = True

        with patch.object(MailTemplate, 'send_mail', check_send_mail_called):
            self.env['website']._send_abandoned_cart_email()
        return email_got_sent

@tagged('post_install', '-at_install')
class TestWebsiteSaleCartAbandoned(TestWebsiteSaleCartAbandonedCommon):
    def test_search_abandoned_cart(self):
        """Make sure the search for abandoned carts uses the delay and public partner specified in each website."""
        SaleOrder = self.env['sale.order']
        abandoned = SaleOrder.search([('is_abandoned_cart', '=', True)]).ids
        self.assertTrue(self.so0before.id in abandoned)
        self.assertTrue(self.so1before.id in abandoned)
        self.assertTrue(self.so1before_but_other_public.id in abandoned)
        self.assertTrue(self.so2before.id in abandoned)
        self.assertFalse(self.so0after.id in abandoned)
        self.assertFalse(self.so1after.id in abandoned)
        self.assertFalse(self.so2after.id in abandoned)
        self.assertFalse(self.so2before_but_public.id in abandoned)

        non_abandoned = SaleOrder.search([('is_abandoned_cart', '=', False)]).ids
        self.assertFalse(self.so0before.id in non_abandoned)
        self.assertFalse(self.so1before.id in non_abandoned)
        self.assertFalse(self.so1before_but_other_public.id in non_abandoned)
        self.assertFalse(self.so2before.id in non_abandoned)
        self.assertTrue(self.so0after.id in non_abandoned)
        self.assertTrue(self.so1after.id in non_abandoned)
        self.assertTrue(self.so2after.id in non_abandoned)
        self.assertFalse(self.so2before_but_public.id in abandoned)

    def test_website_sale_abandoned_cart_email(self):
        """Make sure the send_abandoned_cart_email method sends the correct emails."""

        website = self.env['website'].get_current_website()
        website.send_abandoned_cart_email = True

        product = self.env['product.product'].create({
            'name': 'The Product'
        })
        order_line = [[0, 0, {
            'name': 'The Product',
            'product_id': product.id,
            'product_uom_qty': 1,
        }]]
        abandoned_sale_order = self.env['sale.order'].create({
            'partner_id': self.customer.id,
            'website_id': website.id,
            'state': 'draft',
            'date_order': (datetime.utcnow() - relativedelta(hours=website.cart_abandoned_delay)) - relativedelta(minutes=1),
            'order_line': order_line
        })
        self.assertTrue(abandoned_sale_order.is_abandoned_cart)

        self.assertTrue(self.send_mail_patched(abandoned_sale_order.id))

        # Test that no mail is sent if the partner has no email address.
        self.customer.email = False
        self.env['sale.order'].create({
            'partner_id': self.customer.id,
            'website_id': website.id,
            'state': 'draft',
            'date_order': (datetime.utcnow() - relativedelta(hours=website.cart_abandoned_delay)) - relativedelta(
                minutes=1),
            'order_line': order_line
        })
        self.assertFalse(self.send_mail_patched(abandoned_sale_order.id))

        # Test that no mail is sent if the recovery email of the sale order has already been sent.
        self.env['sale.order'].create({
            'partner_id': self.customer.id,
            'website_id': website.id,
            'state': 'draft',
            'date_order': (datetime.utcnow() - relativedelta(hours=website.cart_abandoned_delay)) - relativedelta(
                minutes=1),
            'order_line': order_line,
            'cart_recovery_email_sent': True
        })
        self.assertFalse(self.send_mail_patched(abandoned_sale_order.id))

        # Test that no email is sent if the sale order contains product that are free.
        free_product_template = self.env['product.template'].create({
            'list_price': 0.0,
            'name': 'free_product'
        })
        free_product_product = free_product_template.product_variant_id
        order_line = [[0, 0, {
            'name': 'The Product',
            'product_id': free_product_product.id,
            'product_uom_qty': 1,
        }]]
        self.env['sale.order'].create({
            'partner_id': self.customer.id,
            'website_id': website.id,
            'state': 'draft',
            'date_order': (datetime.utcnow() - relativedelta(hours=website.cart_abandoned_delay)) - relativedelta(
                minutes=1),
            'order_line': order_line
        })
        self.assertFalse(self.send_mail_patched(abandoned_sale_order.id))

        # Test that no email is sent if the sale order has no error in its transaction.
        abandoned_sale_order = self.env['sale.order'].create({
            'partner_id': self.customer.id,
            'website_id': website.id,
            'state': 'draft',
            'date_order': (datetime.utcnow() - relativedelta(hours=website.cart_abandoned_delay)) - relativedelta(
                minutes=1),
            'order_line': order_line,
        })
        transaction = self.env['payment.transaction'].create({
            'provider_id': 15,
            'payment_method_id': self.payment_method_id,
            'partner_id': self.customer.id,
            'reference': abandoned_sale_order.name,
            'amount': abandoned_sale_order.amount_total,
            'state': 'error',
            'currency_id': self.env.ref('base.EUR').id,

        })
        abandoned_sale_order.transaction_ids += transaction
        self.assertFalse(self.send_mail_patched(abandoned_sale_order.id))

        # Test that if the partner of the abandoned cart made an order ulterior to the abandoned cart create date,
        # no email is sent.
        self.env['sale.order'].create({
            'partner_id': self.customer.id,
            'website_id': website.id,
            'state': 'draft',
            'date_order': (datetime.utcnow() - relativedelta(hours=website.cart_abandoned_delay)) - relativedelta(
                minutes=1),
            'order_line': order_line,
        })
        self.env['sale.order'].create({
            'partner_id': self.customer.id,
            'website_id': website.id,
            'state': 'draft',
            'date_order': datetime.utcnow(),
            'order_line': order_line,
        })
        self.assertFalse(self.send_mail_patched(abandoned_sale_order.id))
