from __future__ import annotations

import unittest
from datetime import datetime, timezone
from unittest.mock import MagicMock, patch

from govoplan_core.celery_app import (
    celery,
    dispatch_platform_events,
    purge_platform_events,
)
from govoplan_core.core.events import PlatformEvent


class PlatformEventWorkerTests(unittest.TestCase):
    def test_dispatch_uses_a_durable_dataflow_consumer_and_commits(self) -> None:
        session = MagicMock()
        database = MagicMock()
        database.SessionLocal.return_value.__enter__.return_value = session
        outbox = MagicMock()
        outbox.dispatch_pending.return_value = {
            "selected": 1,
            "delivered": 1,
            "retrying": 0,
            "quarantined": 0,
            "dispatched": 1,
            "observer_failed": 0,
        }
        dataflow = MagicMock()
        registry = MagicMock()

        with (
            patch(
                "govoplan_core.celery_app._platform_registry",
                return_value=registry,
            ),
            patch(
                "govoplan_core.celery_app._platform_event_outbox",
                return_value=outbox,
            ),
            patch(
                "govoplan_core.celery_app._dataflow_trigger_dispatcher",
                return_value=dataflow,
            ),
            patch(
                "govoplan_core.celery_app._workflow_trigger_dispatcher",
                return_value=None,
            ),
            patch(
                "govoplan_core.db.session.get_database",
                return_value=database,
            ),
        ):
            result = dispatch_platform_events.run(25)

        call = outbox.dispatch_pending.call_args
        self.assertEqual(session, call.args[0])
        self.assertEqual(25, call.kwargs["limit"])
        consumer = call.kwargs["consumers"][0]
        self.assertEqual(
            "dataflow.event-triggers.v1",
            consumer.consumer_id,
        )
        self.assertEqual(
            frozenset({"public", "internal"}),
            consumer.classifications,
        )
        event = PlatformEvent(
            type="files.file.created",
            module_id="files",
        )
        consumer.handler(event, consumer.delivery_key(event))
        dataflow.ingest_event.assert_called_once_with(
            session,
            event=event,
        )
        session.commit.assert_called_once_with()
        self.assertEqual(1, result["delivered"])

    def test_retention_task_uses_the_configured_terminal_window(self) -> None:
        session = MagicMock()
        database = MagicMock()
        database.SessionLocal.return_value.__enter__.return_value = session
        outbox = MagicMock()
        outbox.purge_terminal.return_value = {"deleted": 2}

        with (
            patch(
                "govoplan_core.celery_app._platform_event_outbox",
                return_value=outbox,
            ),
            patch(
                "govoplan_core.db.session.get_database",
                return_value=database,
            ),
            patch(
                "govoplan_core.celery_app.settings."
                "platform_event_outbox_terminal_retention_days",
                30,
            ),
        ):
            result = purge_platform_events.run(75)

        call = outbox.purge_terminal.call_args
        self.assertEqual(session, call.args[0])
        self.assertEqual(75, call.kwargs["limit"])
        before = call.kwargs["before"]
        self.assertIsInstance(before, datetime)
        self.assertEqual(timezone.utc, before.tzinfo)
        session.commit.assert_called_once_with()
        self.assertEqual({"deleted": 2}, result)

    def test_worker_routes_and_periodic_tasks_are_registered(self) -> None:
        self.assertEqual(
            {"queue": "events"},
            celery.conf.task_routes["govoplan.events.dispatch_outbox"],
        )
        self.assertEqual(
            {"queue": "events"},
            celery.conf.task_routes["govoplan.events.purge_outbox"],
        )
        self.assertEqual(
            "govoplan.events.purge_outbox",
            celery.conf.beat_schedule["platform-event-retention-daily"]["task"],
        )


if __name__ == "__main__":
    unittest.main()
