# edited by glg
from pypos.modules.sinkronisasi.services.network_orchestrator_service import (
    NetworkOrchestratorService,
)
from pypos.modules.auth.services.network_probe_service import NetworkProbeService


class _Clock:
    def __init__(self, start=0.0):
        self.value = float(start)

    def now(self):
        return float(self.value)

    def advance(self, seconds):
        self.value += float(seconds)


class _ProbeStub:
    def __init__(self, snapshots):
        self.snapshots = list(snapshots or [])
        self.calls = 0
        self.last_policy = {}

    def probe_network_snapshot(self, policy=None):
        self.calls += 1
        self.last_policy = dict(policy or {})
        if self.snapshots:
            return dict(self.snapshots.pop(0))
        return {
            "raw_state": NetworkProbeService.RAW_OFFLINE,
            "server_online": False,
            "internet_online": False,
        }


def _base_policy(**overrides):
    policy = {
        "enabled": True,
        "probe_cache_ttl_sec": 0.0,
        "fail_threshold": 3,
        "recover_threshold": 1,
        "grace_seconds": 45,
        "allow_unstable_operations": True,
        "allow_degraded_without_server": False,
        "process_allow_internet_only": {
            "sync": False,
            "export": False,
            "settlement_direct": False,
        },
        "server_probe_timeout_sec": 1.0,
        "internet_probe_timeout_sec": 0.8,
        "internet_probe_urls": [],
        "health_check_enabled": False,
        "health_endpoint": "",
        "health_timeout_sec": 1.0,
    }
    policy.update(overrides or {})
    return policy


def test_orchestrator_sync_allow_when_server_stable():
    clock = _Clock(start=10.0)
    probe = _ProbeStub(
        snapshots=[
            {
                "raw_state": NetworkProbeService.RAW_SERVER_OK,
                "server_online": True,
                "internet_online": False,
            }
        ]
    )
    svc = NetworkOrchestratorService(
        probe_service=probe,
        policy_getter=lambda: _base_policy(recover_threshold=1),
        now_fn=clock.now,
    )
    result = svc.can_run(NetworkOrchestratorService.PROCESS_SYNC, force_probe=True)
    assert result["allow"] is True
    assert result["state"] == NetworkOrchestratorService.STATE_ONLINE_STABLE
    assert probe.calls == 1


def test_orchestrator_blocks_sync_when_internet_only_by_default():
    clock = _Clock(start=20.0)
    probe = _ProbeStub(
        snapshots=[
            {
                "raw_state": NetworkProbeService.RAW_INTERNET_ONLY,
                "server_online": False,
                "internet_online": True,
            }
        ]
    )
    svc = NetworkOrchestratorService(
        probe_service=probe,
        policy_getter=lambda: _base_policy(),
        now_fn=clock.now,
    )
    result = svc.can_run(NetworkOrchestratorService.PROCESS_SYNC, force_probe=True)
    assert result["allow"] is False
    assert result["state"] == NetworkOrchestratorService.STATE_INTERNET_ONLY


def test_orchestrator_honors_probe_cache_ttl():
    clock = _Clock(start=30.0)
    probe = _ProbeStub(
        snapshots=[
            {
                "raw_state": NetworkProbeService.RAW_SERVER_OK,
                "server_online": True,
                "internet_online": True,
            }
        ]
    )
    svc = NetworkOrchestratorService(
        probe_service=probe,
        policy_getter=lambda: _base_policy(probe_cache_ttl_sec=60.0, recover_threshold=1),
        now_fn=clock.now,
    )
    first = svc.can_run(NetworkOrchestratorService.PROCESS_EXPORT, force_probe=False)
    second = svc.can_run(NetworkOrchestratorService.PROCESS_EXPORT, force_probe=False)
    assert first["allow"] is True
    assert second["allow"] is True
    assert bool(second.get("cached")) is True
    assert probe.calls == 1


def test_orchestrator_degraded_without_server_blocked_by_default():
    clock = _Clock(start=40.0)
    probe = _ProbeStub(
        snapshots=[
            {
                "raw_state": NetworkProbeService.RAW_SERVER_OK,
                "server_online": True,
                "internet_online": True,
            },
            {
                "raw_state": NetworkProbeService.RAW_INTERNET_ONLY,
                "server_online": False,
                "internet_online": True,
            },
        ]
    )
    svc = NetworkOrchestratorService(
        probe_service=probe,
        policy_getter=lambda: _base_policy(
            recover_threshold=1,
            grace_seconds=120,
            fail_threshold=5,
            allow_unstable_operations=True,
            allow_degraded_without_server=False,
        ),
        now_fn=clock.now,
    )
    first = svc.can_run(NetworkOrchestratorService.PROCESS_SYNC, force_probe=True)
    assert first["allow"] is True
    clock.advance(2.0)
    second = svc.can_run(NetworkOrchestratorService.PROCESS_SYNC, force_probe=True)
    assert second["state"] == NetworkOrchestratorService.STATE_ONLINE_DEGRADED
    assert second["allow"] is False


def test_orchestrator_can_allow_internet_only_for_export_when_enabled():
    clock = _Clock(start=50.0)
    probe = _ProbeStub(
        snapshots=[
            {
                "raw_state": NetworkProbeService.RAW_INTERNET_ONLY,
                "server_online": False,
                "internet_online": True,
            }
        ]
    )
    svc = NetworkOrchestratorService(
        probe_service=probe,
        policy_getter=lambda: _base_policy(
            process_allow_internet_only={
                "sync": False,
                "export": True,
                "settlement_direct": False,
            }
        ),
        now_fn=clock.now,
    )
    result = svc.can_run(NetworkOrchestratorService.PROCESS_EXPORT, force_probe=True)
    assert result["state"] == NetworkOrchestratorService.STATE_INTERNET_ONLY
    assert result["allow"] is True
