import threading
from types import SimpleNamespace

import pytest

from pypos.modules.dashboard.controllers.dashboard_controller import DashboardController

# edited by glg
pytestmark = [pytest.mark.unit]


class _EventRuntimeStub:
    def __init__(self):
        self.calls = 0
        self.last_kwargs = {}

    def run_trigger_cycle(self, **kwargs):
        self.calls += 1
        self.last_kwargs = dict(kwargs or {})
        return {
            "enqueue": {"accepted": True, "ingested_count": 1, "duplicate_count": 0},
            "drain": {"claimed": 1, "processed": 1, "acked": 1, "failed": 0, "purged_sent": 0},
            "cycle_result": {
                "ran": True,
                "exported_rows": 2,
                "uploaded": 1,
                "failed": 0,
                "retried": 0,
                "cleaned_files": 0,
                "cleaned_empty_flux": 0,
                "retry_items": [],
                "failed_items": [],
                "requeued_failed_transient": 0,
                "suppressed_direct_only": 0,
                "suppressed_table_toggle": 0,
            },
        }


def _build_controller_stub():
    ctrl = DashboardController.__new__(DashboardController)
    ctrl.view = SimpleNamespace(export_cycle_service=SimpleNamespace(run_cycle=lambda **_kwargs: {}))
    ctrl.log_info = lambda *_args, **_kwargs: None
    ctrl.log_warning = lambda *_args, **_kwargs: None
    ctrl._export_dispatch_lock = threading.Lock()
    ctrl._pending_export_after_sync = False
    ctrl._schedule_export_recheck = lambda _delay=0: None
    ctrl.is_any_sync_running = lambda: False
    ctrl._can_run_network_operation = lambda *_args, **_kwargs: {"allow": True, "state": "ONLINE_STABLE"}
    ctrl._is_event_ingestion_runtime_enabled = lambda: True
    ctrl._get_event_runtime_numeric_policy = lambda: {
        "dedup_window_seconds": 5,
        "claim_limit": 10,
        "lease_seconds": 60,
        "max_inflight": 1000,
        "retry_delay_seconds": 30,
        "purge_sent_days": 7,
        "purge_limit": 2000,
        "release_expired_each_cycle": True,
    }
    ctrl._build_event_runtime_metrics_payload = lambda source="timer": {
        "pending": 0,
        "inflight": 0,
        "error_rate_pct": 0.0,
        "avg_latency_ms": 10.0,
        "sample_count": 5,
        "source": source,
    }
    ctrl._resolve_sync_device_context = lambda: ("M-01", 101)
    ctrl._export_runtime_metrics = SimpleNamespace(observe_cycle=lambda *_args, **_kwargs: None)
    return ctrl


def test_export_json_batch_menggunakan_runtime_event_first():
    ctrl = _build_controller_stub()
    runtime_stub = _EventRuntimeStub()
    ctrl._get_event_first_export_runtime_service = lambda: runtime_stub
    ctrl._evaluate_export_rollout_runtime_guard = lambda source="timer": {"allow": True, "reason": "healthy"}

    result = ctrl._export_json_batch(
        source="timer",
        upload_limit_override=5,
        requeue_failed_transient=True,
    )

    assert runtime_stub.calls == 1
    assert result["ran"] is True
    assert int(result.get("exported_rows") or 0) == 2
    assert isinstance(result.get("event_runtime"), dict)


def test_export_json_batch_diblok_saat_rollout_guard_halt():
    ctrl = _build_controller_stub()
    runtime_stub = _EventRuntimeStub()
    ctrl._get_event_first_export_runtime_service = lambda: runtime_stub
    scheduled = []
    ctrl._schedule_export_recheck = lambda delay_ms=0: scheduled.append(int(delay_ms))
    ctrl._evaluate_export_rollout_runtime_guard = lambda source="timer": {
        "allow": False,
        "reason": "unhealthy_wave_metrics",
        "decision": "halt_and_rollback",
        "healthy": False,
        "in_scope": True,
        "branch_id": 101,
    }

    result = ctrl._export_json_batch(
        source="timer",
        upload_limit_override=5,
        requeue_failed_transient=False,
    )

    assert result["ran"] is False
    assert result["skipped"] == "rollout_guard_halt"
    assert runtime_stub.calls == 0
    assert scheduled and scheduled[-1] == 5000
