# Part of Odoo. See LICENSE file for full copyright and licensing details.

import pytz

from datetime import datetime, time, timedelta
from freezegun import freeze_time
from unittest.mock import patch

from odoo import fields
from odoo.tests import common

from odoo.addons.lunch.tests.common import TestsCommon


class TestSupplier(TestsCommon):
    def setUp(self):
        super(TestSupplier, self).setUp()

        self.monday_1am = datetime(2018, 10, 29, 1, 0, 0)
        self.monday_10am = datetime(2018, 10, 29, 10, 0, 0)
        self.monday_1pm = datetime(2018, 10, 29, 13, 0, 0)
        self.monday_8pm = datetime(2018, 10, 29, 20, 0, 0)

        self.saturday_3am = datetime(2018, 11, 3, 3, 0, 0)
        self.saturday_10am = datetime(2018, 11, 3, 10, 0, 0)
        self.saturday_1pm = datetime(2018, 11, 3, 13, 0, 0)
        self.saturday_8pm = datetime(2018, 11, 3, 20, 0, 0)

    @common.users('cle-lunch-manager')
    def test_send_email_cron(self):
        self.supplier_kothai.cron_id.ensure_one()
        self.assertEqual(self.supplier_kothai.cron_id.nextcall.time(), time(15, 0))
        self.assertEqual(self.supplier_kothai.cron_id.sudo().code, f"""\
# This cron is dynamically controlled by Lunch Supplier.
# Do NOT modify this cron, modify the related record instead.
env['lunch.supplier'].browse([{self.supplier_kothai.id}])._send_auto_email()""")

        cron_id = self.supplier_kothai.cron_id.id
        self.supplier_kothai.unlink()
        self.assertFalse(self.env['ir.cron'].sudo().search([('id', '=', cron_id)]))

    @common.users('cle-lunch-manager')
    def test_compute_available_today(self):
        tests = [(self.monday_1am, True), (self.monday_10am, True),
                 (self.monday_1pm, True), (self.monday_8pm, True),
                 (self.saturday_3am, False), (self.saturday_10am, False),
                 (self.saturday_1pm, False), (self.saturday_8pm, False)]

        for value, result in tests:
            with patch.object(fields.Datetime, 'now', return_value=value) as _:
                assert self.supplier_pizza_inn.available_today == result,\
                    'supplier pizza inn should %s considered available on %s' % ('be' if result else 'not be', value)

            self.supplier_pizza_inn.invalidate_recordset(['available_today'])

    @common.users('cle-lunch-manager')
    def test_search_available_today(self):
        '''
            This test checks that _search_available_today returns a valid domain.
            The field is of type boolean, so it accepts only the '=' operator.
        '''
        self.env.user.tz = 'Europe/Brussels'
        Supplier = self.env['lunch.supplier']

        tests = [(self.monday_1am, 1.0, 'mon'), (self.monday_10am, 10.0, 'mon'),
                 (self.monday_1pm, 13.0, 'mon'), (self.monday_8pm, 20.0, 'mon'),
                 (self.saturday_3am, 3.0, 'sat'), (self.saturday_10am, 10.0, 'sat'),
                 (self.saturday_1pm, 13.0, 'sat'), (self.saturday_8pm, 20.0, 'sat')]

        for value, rvalue, dayname in tests:
            with self.subTest(value=value), freeze_time(value):
                self.assertEqual(
                    list(Supplier._search_available_today('in', [True])),
                    ['&', '|', ('recurrency_end_date', '=', False),
                        ('recurrency_end_date', '>', value.astimezone(pytz.timezone(self.env.user.tz)).date()),
                        (dayname, 'in', [True])],
                )

        with patch.object(fields.Datetime, 'now', return_value=self.monday_10am):
            self.assertIn(self.supplier_pizza_inn, Supplier.search([('available_today', '=', True)]))

    @common.users('cle-lunch-manager')
    def test_auto_email_send(self):
        with patch.object(fields.Datetime, 'now', return_value=self.monday_1pm) as _:
            with patch.object(fields.Date, 'today', return_value=self.monday_1pm.date()) as _:
                with patch.object(fields.Date, 'context_today', return_value=self.monday_1pm.date()) as _:
                    line_pizza = self.env['lunch.order'].create({
                        'product_id': self.product_pizza.id,
                        'date': self.monday_1pm.date(),
                        'supplier_id': self.supplier_pizza_inn.id,
                    })

                    line_pizza.action_order()
                    assert line_pizza.state == 'ordered'

                    self.supplier_pizza_inn._send_auto_email()

                    assert line_pizza.state == 'sent'

                    line_pizza_olive = self.env['lunch.order'].create({
                        'product_id': self.product_pizza.id,
                        'topping_ids_1': [(6, 0, [self.topping_olives.id])],
                        'date': self.monday_1pm.date(),
                        'supplier_id': self.supplier_pizza_inn.id,
                    })
                    line_tuna = self.env['lunch.order'].create({
                        'product_id': self.product_sandwich_tuna.id,
                        'date': self.monday_1pm.date(),
                        'supplier_id': self.supplier_coin_gourmand.id,
                    })

                    (line_pizza_olive | line_tuna).action_order()
                    assert line_pizza_olive.state == 'ordered'
                    assert line_tuna.state == 'ordered'

                    self.supplier_pizza_inn._send_auto_email()

                    assert line_pizza_olive.state == 'sent'
                    assert line_tuna.state == 'ordered'

                    line_pizza_2 = self.env['lunch.order'].create({
                        'product_id': self.product_pizza.id,
                        'quantity': 2,
                        'date': self.monday_1pm.date(),
                        'supplier_id': self.supplier_pizza_inn.id,
                    })

                    line_pizza_olive_2 = self.env['lunch.order'].create({
                        'product_id': self.product_pizza.id,
                        'topping_ids_1': [(6, 0, [self.topping_olives.id])],
                        'date': self.monday_1pm.date(),
                        'supplier_id': self.supplier_pizza_inn.id,
                    })

                    line_tuna_2 = self.env['lunch.order'].create({
                        'product_id': self.product_sandwich_tuna.id,
                        'quantity': 2,
                        'date': self.monday_1pm.date(),
                        'supplier_id': self.supplier_coin_gourmand.id,
                    })

                    ######################################################
                    # id:  # lines:               # state:      # quantity:
                    #######################################################
                    # 1    # line_pizza           # sent        # 1
                    # 2    # line_pizza_olive     # sent        # 1
                    # 3    # line_tuna            # ordered     # 1
                    # 4    # line_pizza_2         # new         # 2
                    # 5    # line_pizza_olive_2   # new         # 1
                    # 6    # line_tuna_2          # new         # 2

                    (line_pizza_2 | line_pizza_olive_2 | line_tuna_2).action_order()

                    ######################################################
                    # id:  # lines:               # state:      # quantity:
                    #######################################################
                    # 1    # line_pizza           # sent        # 1
                    # 2    # line_pizza_olive     # sent        # 1
                    # 3    # line_tuna            # ordered     # 3 (1 + 2 from line_tuna_2 id=6)
                    # 4    # line_pizza_2         # ordered     # 2
                    # 5    # line_pizza_olive_2   # ordered     # 1

                    assert all(line.state == 'ordered' for line in [line_pizza_2, line_pizza_olive_2])

                    self.assertEqual(line_tuna_2.active, False)
                    self.assertEqual(line_tuna.quantity, 3)

                    self.supplier_pizza_inn._send_auto_email()

    @common.users('cle-lunch-manager')
    def test_cron_sync_create(self):
        cron_ny = self.supplier_kothai.cron_id  # I am at New-York
        self.assertTrue(cron_ny.active)
        self.assertEqual(cron_ny.name, "Lunch: send automatic email to Kothai")
        self.assertEqual(
            [line for line in cron_ny.sudo().code.splitlines() if not line.lstrip().startswith("#")],
            ["env['lunch.supplier'].browse([%i])._send_auto_email()" % self.supplier_kothai.id])
        self.assertEqual(cron_ny.nextcall, datetime(2021, 1, 29, 15, 0))  # New-york is UTC-5

    @common.users('cle-lunch-manager')
    def test_cron_sync_active(self):
        cron_ny = self.supplier_kothai.cron_id

        self.supplier_kothai.active = False
        self.assertFalse(cron_ny.active)
        self.supplier_kothai.active = True
        self.assertTrue(cron_ny.active)

        self.supplier_kothai.send_by = 'phone'
        self.assertFalse(cron_ny.active)
        self.supplier_kothai.send_by = 'mail'
        self.assertTrue(cron_ny.active)

    @common.users('cle-lunch-manager')
    def test_cron_sync_nextcall(self):
        cron_ny = self.supplier_kothai.cron_id
        old_nextcall = cron_ny.nextcall

        self.supplier_kothai.automatic_email_time -= 5
        self.assertEqual(cron_ny.nextcall, old_nextcall - timedelta(hours=5) + timedelta(days=1))

        # Simulate cron execution
        cron_ny.sudo().lastcall = old_nextcall - timedelta(hours=5)
        cron_ny.sudo().nextcall += timedelta(days=1)

        self.supplier_kothai.automatic_email_time += 7
        self.assertEqual(cron_ny.nextcall, old_nextcall + timedelta(days=1, hours=2))

        self.supplier_kothai.automatic_email_time -= 1
        self.assertEqual(cron_ny.nextcall, old_nextcall + timedelta(days=1, hours=1))

    def test_remove_toppings(self):
        partner = self.env['res.partner'].create({
                'name': 'Partner',
            })

        supplier = self.env['lunch.supplier'].create({
            'partner_id': partner.id,
            'send_by': 'phone',
            'topping_ids_2': [
                (0, 0, {
                    'name': 'salt',
                    'price': 7,
                    'company_id': self.env.company.id
                }),
            ],
            'topping_ids_3': [
                (0, 0, {
                    'name': 'sugar',
                    'price': 10,
                    'company_id': self.env.company.id
                }),
            ],
        })

        # simulating the delete as it's done on frontend
        supplier.write({
            'topping_ids_2': [(2, supplier.topping_ids_2.id)],
        })
        self.assertFalse(supplier.topping_ids_2)

        # simulating the delete as it's done on frontend
        supplier.write({
            'topping_ids_3': [(2, supplier.topping_ids_3.id)],
        })
        self.assertFalse(supplier.topping_ids_3)

    def test_lunch_order_with_minimum_threshold(self):
        """ Test that lunch order is allowed within the overdraft threshold. """

        self.env.company.lunch_minimum_threshold = 200.0
        order = self.env['lunch.order'].create({
            'product_id': self.product_pizza.id,
            'date': self.monday_1pm.date(),
            'supplier_id': self.supplier_pizza_inn.id,
            'quantity': 11,
        })
        self.assertTrue(order.display_add_button)

        order.action_order()
        self.assertEqual(order.state, "ordered")

    def test_order_with_vendor_recurrency_end_date(self):
        """ Test lunch order when vendor have recurrency_end_date field set """
        self.supplier_pizza_inn.recurrency_end_date = self.monday_1pm.date() + timedelta(days=7)

        # Test _compute_available_today flow (datetime.datetime object)
        with patch.object(fields.Datetime, 'now', return_value=self.monday_1pm):
            self.supplier_pizza_inn.invalidate_recordset(['available_today'])
            self.assertTrue(self.supplier_pizza_inn.available_today)

        # Test _compute_available_on_date flow (datetime.date object)
        order = self.env['lunch.order'].create({
            'product_id': self.product_pizza.id,
            'date': self.monday_1pm.date(),
            'supplier_id': self.supplier_pizza_inn.id,
        })
        self.assertTrue(order.available_on_date)
