"""HTTP tests for POST /api/widget/event + the event batching pipeline.

Events are buffered in `_event_queue` and flushed every 10s via a
threading.Timer to amortise the write cost at 1000+ affiliate scale.
These tests confirm queue depth grows on impression/click, flushing
materialises rows in `affiliate.widget.event`, and the impression dedup
(per config + ip) suppresses duplicates within 5 minutes.
"""

import json

from odoo.tests import tagged

from odoo.addons.kj_affiliate_widget.controllers import widget_api as _wa
from odoo.addons.kj_affiliate_widget.tests.common import WidgetTestCommon


@tagged("post_install", "-at_install", "kj_affiliate_widget")
class TestWidgetEvent(WidgetTestCommon):

    def _post_event(self, type_, key=None, **extra):
        body = {"key": key or self.config.widget_key, "type": type_}
        body.update(extra)
        return self.url_open(
            "/api/widget/event",
            data=json.dumps(body),
            headers={"Content-Type": "application/json"},
        )

    def test_click_event_queues_then_flushes(self):
        Event = self.env["affiliate.widget.event"].sudo()
        before = Event.search_count(
            [("config_id", "=", self.config.id), ("event_type", "=", "click")]
        )
        r = self._post_event("click", job_id=self.job.id)
        self.assertEqual(r.status_code, 200)
        # Queue depth should have grown.
        with _wa._event_queue_lock:
            self.assertGreaterEqual(_wa._event_stats["queued"], 1)
            self.assertEqual(len(_wa._event_queue), 1)
        # Force-flush instead of waiting 10 s.
        _wa._flush_event_queue()
        after = Event.search_count(
            [("config_id", "=", self.config.id), ("event_type", "=", "click")]
        )
        self.assertEqual(after, before + 1)

    def test_impression_dedup_within_window(self):
        """Two impressions from the same ip+config in quick succession
        only count once — `_should_record_impression` blocks the second."""
        self._post_event("impression")
        self._post_event("impression")
        with _wa._event_queue_lock:
            queued = _wa._event_stats["queued"]
        self.assertEqual(
            queued, 1,
            "duplicate impression within dedup window must not enqueue",
        )

    def test_invalid_event_type_rejected(self):
        r = self._post_event("not-a-real-type")
        self.assertEqual(r.status_code, 400)

    def test_missing_key_rejected(self):
        r = self.url_open(
            "/api/widget/event",
            data=json.dumps({"type": "click"}),
            headers={"Content-Type": "application/json"},
        )
        self.assertEqual(r.status_code, 400)

    def test_invalid_key_rejected(self):
        r = self._post_event("click", key="nope-not-a-key")
        self.assertEqual(r.status_code, 403)

    def test_overflow_drops_oldest(self):
        """The queue is bounded — once full, the oldest entries are
        dropped (better than blocking page requests on DB pressure)."""
        # Fill the queue past the cap.
        with _wa._event_queue_lock:
            _wa._event_queue.clear()
            _wa._event_stats["dropped"] = 0
        cap = _wa._EVENT_QUEUE_MAX
        for i in range(cap + 5):
            _wa._enqueue_event({
                "config_id": self.config.id,
                "event_type": "click",
                "referrer_url": f"https://example.com/{i}",
                "visitor_ip": "127.0.0.1",
            })
        with _wa._event_queue_lock:
            self.assertEqual(len(_wa._event_queue), cap)
            self.assertEqual(_wa._event_stats["dropped"], 5)
