# edited by glg
import unittest
from unittest.mock import patch

from pypos.app import AppController


class _DummyConfigService:
    def __init__(self, direct_only_mode=True):
        self._direct_only_mode = bool(direct_only_mode)

    def is_settlement_direct_only_mode(self):
        return bool(self._direct_only_mode)


class _DummyDashboardController:
    def __init__(
        self,
        state,
        direct_only_mode=True,
        retry_result=None,
        state_after_retry=None,
    ):
        self.config_service = _DummyConfigService(direct_only_mode=direct_only_mode)
        self._state = dict(state or {})
        self._retry_result = (
            dict(retry_result)
            if isinstance(retry_result, dict)
            else {
                "attempted": 0,
                "success": 0,
                "failed": 0,
                "inflight": False,
                "remaining": int(self._state.get("unsent_count") or 0),
            }
        )
        self._state_after_retry = (
            dict(state_after_retry) if isinstance(state_after_retry, dict) else None
        )
        self.retry_calls = 0
        self.retry_max_items_seen = []

    def get_settlement_delivery_guard_state(self):
        return dict(self._state)

    def retry_pending_settlement_direct_delivery(self, max_items=3):
        self.retry_calls += 1
        self.retry_max_items_seen.append(int(max_items))
        if self._state_after_retry is not None:
            self._state = dict(self._state_after_retry)
        return dict(self._retry_result)


class _DummyDashboardWindow:
    def __init__(self, controller):
        self.dashboard_controller = controller


class _DummyAppContext:
    def __init__(self, dashboard_controller=None):
        self.dashboard_window = (
            _DummyDashboardWindow(dashboard_controller)
            if dashboard_controller is not None
            else None
        )

    def _is_settlement_close_guard_enabled(self, cfg):
        return AppController._is_settlement_close_guard_enabled(self, cfg)

    def _build_settlement_close_guard_message(self, state):
        return AppController._build_settlement_close_guard_message(self, state)


class AppCloseSettlementGuardTests(unittest.TestCase):
    def test_close_guard_returns_false_when_mode_not_direct_only(self):
        state = {"inflight": False, "unsent_count": 1, "unsent_items": [], "blocking": True}
        controller = _DummyDashboardController(state=state, direct_only_mode=False)
        app_ctx = _DummyAppContext(dashboard_controller=controller)

        with patch("pypos.app.QMessageBox.warning") as warning_mock:
            blocked = AppController._should_block_close_for_settlement_delivery(
                app_ctx,
                {"settlement_close_guard_enabled": 1},
            )
        self.assertFalse(blocked)
        warning_mock.assert_not_called()

    def test_close_guard_blocks_and_shows_warning_when_unsent_exists(self):
        state = {
            "inflight": False,
            "unsent_count": 1,
            "unsent_items": [
                {
                    "settlement_result": {"counter": "ST-20260311-901", "transaksi_ids": [1]},
                    "error": "offline",
                }
            ],
            "blocking": True,
        }
        controller = _DummyDashboardController(state=state, direct_only_mode=True)
        app_ctx = _DummyAppContext(dashboard_controller=controller)

        with patch("pypos.app.QMessageBox.warning") as warning_mock:
            blocked = AppController._should_block_close_for_settlement_delivery(
                app_ctx,
                {
                    "settlement_close_guard_enabled": 1,
                    "settlement_close_guard_retry_before_block": 0,
                    "settlement_close_guard_title": "Penutupan Ditunda",
                },
            )
        self.assertTrue(blocked)
        warning_mock.assert_called_once()

    def test_close_guard_allows_close_when_retry_clears_pending(self):
        state_before = {
            "inflight": False,
            "unsent_count": 1,
            "unsent_items": [
                {
                    "settlement_result": {"counter": "ST-20260311-902", "transaksi_ids": [2]},
                    "error": "timeout",
                }
            ],
            "blocking": True,
        }
        state_after = {
            "inflight": False,
            "unsent_count": 0,
            "unsent_items": [],
            "blocking": False,
        }
        retry_result = {
            "attempted": 1,
            "success": 1,
            "failed": 0,
            "inflight": False,
            "remaining": 0,
        }
        controller = _DummyDashboardController(
            state=state_before,
            direct_only_mode=True,
            retry_result=retry_result,
            state_after_retry=state_after,
        )
        app_ctx = _DummyAppContext(dashboard_controller=controller)

        with patch("pypos.app.QMessageBox.warning") as warning_mock:
            blocked = AppController._should_block_close_for_settlement_delivery(
                app_ctx,
                {
                    "settlement_close_guard_enabled": 1,
                    "settlement_close_guard_retry_before_block": 1,
                    "settlement_close_guard_retry_max_items": 3,
                },
            )
        self.assertFalse(blocked)
        self.assertEqual(controller.retry_calls, 1)
        self.assertEqual(controller.retry_max_items_seen, [3])
        warning_mock.assert_not_called()

    def test_close_guard_blocks_when_retry_still_pending(self):
        state_before = {
            "inflight": False,
            "unsent_count": 2,
            "unsent_items": [
                {
                    "settlement_result": {"counter": "ST-20260311-903", "transaksi_ids": [3]},
                    "error": "server busy",
                }
            ],
            "blocking": True,
        }
        state_after = dict(state_before)
        retry_result = {
            "attempted": 1,
            "success": 0,
            "failed": 1,
            "inflight": False,
            "remaining": 2,
        }
        controller = _DummyDashboardController(
            state=state_before,
            direct_only_mode=True,
            retry_result=retry_result,
            state_after_retry=state_after,
        )
        app_ctx = _DummyAppContext(dashboard_controller=controller)

        with patch("pypos.app.QMessageBox.warning") as warning_mock:
            blocked = AppController._should_block_close_for_settlement_delivery(
                app_ctx,
                {
                    "settlement_close_guard_enabled": 1,
                    "settlement_close_guard_retry_before_block": 1,
                    "settlement_close_guard_retry_max_items": 2,
                },
            )
        self.assertTrue(blocked)
        self.assertEqual(controller.retry_calls, 1)
        self.assertEqual(controller.retry_max_items_seen, [2])
        warning_mock.assert_called_once()

