# Copyright 2025 Criptomart
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)

"""
Test suite for validations and constraints in website_sale_aplicoop.

Coverage:
- group.order constraint: same company for all groups
- group.order constraint: start_date < end_date
- group.order computed field: image_1920 fallback logic
- group.order computed field: product count
- res.partner validation: user without partner_id
- group.order state transitions: illegal transitions
"""

from datetime import datetime
from datetime import timedelta

from odoo.exceptions import UserError
from odoo.exceptions import ValidationError
from odoo.tests.common import TransactionCase


class TestGroupOrderValidations(TransactionCase):
    """Test constraints and validations for group.order model."""

    def setUp(self):
        super().setUp()
        self.company1 = self.env.company
        self.company2 = self.env["res.company"].create(
            {
                "name": "Company 2",
            }
        )

        self.group_c1 = self.env["res.partner"].create(
            {
                "name": "Group Company 1",
                "is_company": True,
                "company_id": self.company1.id,
            }
        )

        self.group_c2 = self.env["res.partner"].create(
            {
                "name": "Group Company 2",
                "is_company": True,
                "company_id": self.company2.id,
            }
        )

    def test_group_order_same_company_constraint(self):
        """Test that all groups in an order must be from same company."""
        start_date = datetime.now().date()

        # Creating order with groups from different companies should fail
        with self.assertRaises(ValidationError):
            self.env["group.order"].create(
                {
                    "name": "Multi-Company Order",
                    "group_ids": [(6, 0, [self.group_c1.id, self.group_c2.id])],
                    "type": "regular",
                    "start_date": start_date,
                    "end_date": start_date + timedelta(days=7),
                    "period": "weekly",
                    "pickup_day": "3",
                    "cutoff_day": "0",
                }
            )

    def test_group_order_same_company_mixed_single(self):
        """Test that single company group is valid."""
        start_date = datetime.now().date()

        # Single company should pass
        order = self.env["group.order"].create(
            {
                "name": "Single Company Order",
                "group_ids": [(6, 0, [self.group_c1.id])],
                "type": "regular",
                "start_date": start_date,
                "end_date": start_date + timedelta(days=7),
                "period": "weekly",
                "pickup_day": "3",
                "cutoff_day": "0",
            }
        )
        self.assertTrue(order.exists())

    def test_group_order_date_validation_start_after_end(self):
        """Test that start_date must be before end_date."""
        start_date = datetime.now().date()
        end_date = start_date - timedelta(days=1)  # End before start

        with self.assertRaises(ValidationError):
            self.env["group.order"].create(
                {
                    "name": "Bad Dates Order",
                    "group_ids": [(6, 0, [self.group_c1.id])],
                    "type": "regular",
                    "start_date": start_date,
                    "end_date": end_date,
                    "period": "weekly",
                    "pickup_day": "3",
                    "cutoff_day": "0",
                }
            )

    def test_group_order_date_validation_same_date(self):
        """Test that start_date == end_date is allowed (single-day order)."""
        same_date = datetime.now().date()

        order = self.env["group.order"].create(
            {
                "name": "Same Day Order",
                "group_ids": [(6, 0, [self.group_c1.id])],
                "type": "regular",
                "start_date": same_date,
                "end_date": same_date,
                "period": "once",
                "pickup_day": "0",
                "cutoff_day": "0",
            }
        )
        self.assertTrue(order.exists())


class TestGroupOrderImageFallback(TransactionCase):
    """Test image_1920 computed field fallback logic."""

    def setUp(self):
        super().setUp()
        self.group = self.env["res.partner"].create(
            {
                "name": "Test Group",
                "is_company": True,
            }
        )

        start_date = datetime.now().date()
        self.group_order = self.env["group.order"].create(
            {
                "name": "Test Order",
                "group_ids": [(6, 0, [self.group.id])],
                "type": "regular",
                "start_date": start_date,
                "end_date": start_date + timedelta(days=7),
                "period": "weekly",
                "pickup_day": "3",
                "cutoff_day": "0",
            }
        )

    def test_image_fallback_order_image_first(self):
        """Test that order image takes priority over group image."""
        # Set both order and group image
        test_image = b"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="

        self.group_order.image_1920 = test_image
        self.group.image_1920 = test_image

        # Order image should be returned
        computed_image = self.group_order.image_1920
        self.assertEqual(computed_image, test_image)

    def test_image_fallback_group_image_when_no_order_image(self):
        """Test fallback to group image when order has no image."""
        test_image = b"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="

        # Only set group image
        self.group_order.image_1920 = False
        self.group.image_1920 = test_image

        # Group image should be returned as fallback
        # Note: This requires the computed field logic to be tested
        # after field recalculation

    def test_image_fallback_none_when_no_images(self):
        """Test that None is returned when no image available."""
        # No images set
        self.group_order.image_1920 = False
        self.group.image_1920 = False

        # Should be empty/False
        computed_image = self.group_order.image_1920
        self.assertFalse(computed_image)


class TestGroupOrderProductCount(TransactionCase):
    """Test product_count computed field."""

    def setUp(self):
        super().setUp()
        self.group = self.env["res.partner"].create(
            {
                "name": "Test Group",
                "is_company": True,
            }
        )

        start_date = datetime.now().date()
        self.group_order = self.env["group.order"].create(
            {
                "name": "Test Order",
                "group_ids": [(6, 0, [self.group.id])],
                "type": "regular",
                "start_date": start_date,
                "end_date": start_date + timedelta(days=7),
                "period": "weekly",
                "pickup_day": "3",
                "cutoff_day": "0",
            }
        )

        self.product1 = self.env["product.product"].create(
            {
                "name": "Product 1",
                "type": "consu",
                "list_price": 10.0,
            }
        )

        self.product2 = self.env["product.product"].create(
            {
                "name": "Product 2",
                "type": "consu",
                "list_price": 20.0,
            }
        )

    def test_product_count_initial_zero(self):
        """Test that new order has zero products."""
        self.assertEqual(self.group_order.product_count, 0)

    def test_product_count_increments_on_add(self):
        """Test that product_count increases when adding products."""
        self.group_order.product_ids = [(4, self.product1.id)]
        self.assertEqual(self.group_order.product_count, 1)

        self.group_order.product_ids = [(4, self.product2.id)]
        self.assertEqual(self.group_order.product_count, 2)

    def test_product_count_decrements_on_remove(self):
        """Test that product_count decreases when removing products."""
        self.group_order.product_ids = [(6, 0, [self.product1.id, self.product2.id])]
        self.assertEqual(self.group_order.product_count, 2)

        self.group_order.product_ids = [(3, self.product1.id)]
        self.assertEqual(self.group_order.product_count, 1)

    def test_product_count_all_removed(self):
        """Test that product_count is zero when all removed."""
        self.group_order.product_ids = [(6, 0, [self.product1.id, self.product2.id])]
        self.group_order.product_ids = [(6, 0, [])]
        self.assertEqual(self.group_order.product_count, 0)


class TestStateTransitions(TransactionCase):
    """Test group.order state transition validation."""

    def setUp(self):
        super().setUp()
        self.group = self.env["res.partner"].create(
            {
                "name": "Test Group",
                "is_company": True,
            }
        )

        start_date = datetime.now().date()
        self.order = self.env["group.order"].create(
            {
                "name": "Test Order",
                "group_ids": [(6, 0, [self.group.id])],
                "type": "regular",
                "start_date": start_date,
                "end_date": start_date + timedelta(days=7),
                "period": "weekly",
                "pickup_day": "3",
                "cutoff_day": "0",
            }
        )

    def test_illegal_transition_draft_to_closed(self):
        """Test that Draft -> Closed transition is not allowed."""
        # Should not allow skipping Open state
        self.assertEqual(self.order.state, "draft")

        # Calling action_close() without action_open() should fail
        with self.assertRaises((ValidationError, UserError)):
            self.order.action_close()

    def test_illegal_transition_cancelled_to_open(self):
        """Test that Cancelled -> Open transition is not allowed."""
        self.order.action_cancel()
        self.assertEqual(self.order.state, "cancelled")

        # Should not allow re-opening cancelled order
        with self.assertRaises((ValidationError, UserError)):
            self.order.action_open()

    def test_legal_transition_draft_open_closed(self):
        """Test that Draft -> Open -> Closed is allowed."""
        self.assertEqual(self.order.state, "draft")

        self.order.action_open()
        self.assertEqual(self.order.state, "open")

        self.order.action_close()
        self.assertEqual(self.order.state, "closed")

    def test_transition_draft_to_cancelled(self):
        """Test that Draft -> Cancelled is allowed."""
        self.assertEqual(self.order.state, "draft")

        self.order.action_cancel()
        self.assertEqual(self.order.state, "cancelled")

    def test_transition_open_to_cancelled(self):
        """Test that Open -> Cancelled is allowed (emergency stop)."""
        self.order.action_open()
        self.assertEqual(self.order.state, "open")

        self.order.action_cancel()
        self.assertEqual(self.order.state, "cancelled")


class TestUserPartnerValidation(TransactionCase):
    """Test validation when user has no partner_id."""

    def setUp(self):
        super().setUp()
        self.group = self.env["res.partner"].create(
            {
                "name": "Test Group",
                "is_company": True,
            }
        )

        # Create user without partner (edge case)
        self.user_no_partner = self.env["res.users"].create(
            {
                "name": "User No Partner",
                "login": "noparnter@test.com",
                "partner_id": False,  # Explicitly no partner
            }
        )

    def test_user_without_partner_cannot_access_order(self):
        """Test that user without partner_id has no access to orders."""
        start_date = datetime.now().date()
        self.env["group.order"].create(
            {
                "name": "Test Order",
                "group_ids": [(6, 0, [self.group.id])],
                "type": "regular",
                "start_date": start_date,
                "end_date": start_date + timedelta(days=7),
                "period": "weekly",
                "pickup_day": "3",
                "cutoff_day": "0",
            }
        )

        # User without partner should not have access
        # This should be validated in controller
        self.assertFalse(self.user_no_partner.partner_id)
