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

import json
from datetime import timedelta
from types import SimpleNamespace
from unittest.mock import patch

from odoo import fields
from odoo.tests.common import TransactionCase

from ..controllers.website_sale import AplicoopWebsiteSale


class TestGroupOrderStatusEndpoint(TransactionCase):
    """Tests for status endpoint and closed-order safeguards in cart endpoints."""

    def setUp(self):
        super().setUp()
        self.controller = AplicoopWebsiteSale()

        self.group = self.env["res.partner"].create(
            {
                "name": "Status Group",
                "is_company": True,
                "is_group": True,
                "email": "status-group@test.com",
            }
        )

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

        self.product = self.env["product.product"].create(
            {
                "name": "Status Endpoint Product",
                "type": "consu",
                "list_price": 12.0,
            }
        )

    def _build_request_mock(self, payload):
        """Build a request mock with JSON payload and make_response support."""

        def _make_response(data, headers=None, status=200):
            raw = data.encode("utf-8") if isinstance(data, str) else data
            return SimpleNamespace(data=raw, status=status, headers=headers or [])

        return SimpleNamespace(
            env=self.env,
            httprequest=SimpleNamespace(
                data=json.dumps(payload).encode("utf-8"),
            ),
            make_response=_make_response,
        )

    def test_check_group_order_status_open(self):
        """Endpoint returns is_open=True for open group order."""
        request_mock = self._build_request_mock({"order_id": self.group_order.id})

        with patch(
            "odoo.addons.website_sale_aplicoop.controllers.website_sale.request",
            request_mock,
        ):
            response = self.controller.check_group_order_status.__wrapped__(
                self.controller
            )

        data = json.loads(response.data.decode("utf-8"))
        self.assertTrue(data["success"])
        self.assertTrue(data["is_open"])
        self.assertEqual(data["state"], "open")
        self.assertEqual(data["action"], "none")

    def test_check_group_order_status_closed(self):
        """Endpoint returns clear_cart action for non-open group order."""
        self.group_order.action_close()
        request_mock = self._build_request_mock({"order_id": self.group_order.id})

        with patch(
            "odoo.addons.website_sale_aplicoop.controllers.website_sale.request",
            request_mock,
        ):
            response = self.controller.check_group_order_status.__wrapped__(
                self.controller
            )

        data = json.loads(response.data.decode("utf-8"))
        self.assertTrue(data["success"])
        self.assertFalse(data["is_open"])
        self.assertEqual(data["state"], "closed")
        self.assertEqual(data["action"], "clear_cart")

    def test_load_draft_endpoint_rejects_when_cutoff_passed(self):
        """/eskaera/load-draft must refuse to load drafts when cutoff_date is in
        the past, even if the group.order is still in 'open' state.

        Regression: previously the endpoint only checked group_order.state, so
        when cutoff had passed the previous cycle's draft was returned and
        repopulated the cart on the next page load.
        """
        # Force cutoff_date into the past. The @api.depends compute always
        # rolls forward to the next occurrence, so we have to write directly
        # to the stored field to simulate a stale cycle.
        past = fields.Date.today() - timedelta(days=1)
        self.group_order.sudo().write({"cutoff_date": past})
        # Sanity: state remains open; only cutoff is stale.
        self.assertEqual(self.group_order.state, "open")
        self.assertLess(self.group_order.cutoff_date, fields.Date.today())

        payload = {"order_id": self.group_order.id}
        request_mock = self._build_request_mock(payload)

        with patch(
            "odoo.addons.website_sale_aplicoop.controllers.website_sale.request",
            request_mock,
        ):
            response = self.controller.load_draft_cart.__wrapped__(self.controller)

        data = json.loads(response.data.decode("utf-8"))
        self.assertEqual(response.status, 403)
        self.assertEqual(data.get("code"), "group_order_not_open")
        self.assertEqual(data.get("action"), "clear_cart")
        self.assertNotIn("items", data)

    def test_find_recent_draft_excludes_previous_cycle(self):
        """_find_recent_draft_order must only return drafts whose create_date
        falls in [cutoff_date - 6 days, cutoff_date].

        Regression (observed on stage.elikabilbo.eus): when the previous
        cycle's draft still matched group_order.pickup_date (because both
        were the same stale value), the helper returned it and the cart
        was repopulated with old products on the next page load.
        """
        partner = self.env.user.partner_id

        # Ensure cutoff_date is in the (near) future so the active window is
        # well-defined and the cutoff-passed guard is NOT what's being tested.
        future_cutoff = fields.Date.today() + timedelta(days=2)
        self.group_order.sudo().write({"cutoff_date": future_cutoff})
        # Mirror production conditions where pickup_date matches between
        # group_order and the stale draft — this is what made the old
        # exact-pickup-date filter return the previous cycle's draft.
        matching_pickup_date = self.group_order.pickup_date

        previous_cycle_create = fields.Datetime.now() - timedelta(days=10)
        stale_draft = self.env["sale.order"].create(
            {
                "partner_id": partner.id,
                "group_order_id": self.group_order.id,
                "pickup_date": matching_pickup_date,
                "order_line": [
                    (
                        0,
                        0,
                        {
                            "product_id": self.product.id,
                            "product_uom_qty": 3,
                        },
                    )
                ],
            }
        )
        # create_date is auto-stamped; backdate it directly to simulate a draft
        # left over from a previous weekly cycle.
        self.env.cr.execute(
            "UPDATE sale_order SET create_date = %s WHERE id = %s",
            (previous_cycle_create, stale_draft.id),
        )
        stale_draft.invalidate_recordset(["create_date"])

        request_mock = self._build_request_mock({})
        with patch(
            "odoo.addons.website_sale_aplicoop.controllers.website_sale.request",
            request_mock,
        ):
            found = self.controller._find_recent_draft_order(
                partner.id, self.group_order
            )
        self.assertFalse(
            found,
            "Previous-cycle draft must not be returned (create_date is outside "
            "[cutoff-6, cutoff] window) even when pickup_date matches",
        )

        # Now create a current-cycle draft and confirm it IS returned.
        current_draft = self.env["sale.order"].create(
            {
                "partner_id": partner.id,
                "group_order_id": self.group_order.id,
                "pickup_date": matching_pickup_date,
                "order_line": [
                    (
                        0,
                        0,
                        {
                            "product_id": self.product.id,
                            "product_uom_qty": 1,
                        },
                    )
                ],
            }
        )
        with patch(
            "odoo.addons.website_sale_aplicoop.controllers.website_sale.request",
            request_mock,
        ):
            found = self.controller._find_recent_draft_order(
                partner.id, self.group_order
            )
        self.assertEqual(found.id, current_draft.id)

    def test_save_order_endpoint_rejects_closed_group_order(self):
        """/eskaera/save-order must reject closed group order with clear_cart action."""
        self.group_order.action_close()

        payload = {
            "order_id": self.group_order.id,
            "items": [
                {
                    "product_id": self.product.id,
                    "quantity": 1,
                    "product_price": 12.0,
                }
            ],
        }
        request_mock = self._build_request_mock(payload)

        with patch(
            "odoo.addons.website_sale_aplicoop.controllers.website_sale.request",
            request_mock,
        ):
            response = self.controller.save_eskaera_draft.__wrapped__(self.controller)

        data = json.loads(response.data.decode("utf-8"))
        self.assertEqual(response.status, 403)
        self.assertEqual(data.get("code"), "group_order_not_open")
        self.assertEqual(data.get("order_state"), "closed")
        self.assertEqual(data.get("action"), "clear_cart")
