import json

import requests

from pypos.modules.auth.services.network_probe_service import NetworkProbeService


class _Resp:
    def __init__(self, status_code=200, text=""):
        self.status_code = status_code
        self.text = text
        self._closed = False

    def close(self):
        self._closed = True


class _FakeHttp:
    def __init__(self, routes):
        self.routes = dict(routes or {})
        self.calls = []

    def request(self, method, url, timeout=None, allow_redirects=True, stream=True):
        key = (str(method).upper(), str(url))
        self.calls.append(key)
        action = self.routes.get(key)
        if isinstance(action, Exception):
            raise action
        if callable(action):
            return action()
        if action is None:
            raise requests.ConnectionError(f"no-route:{key}")
        return action


def _policy(**override):
    base = {
        "server_probe_timeout_sec": 0.5,
        "internet_probe_timeout_sec": 0.5,
        "internet_probe_urls": ["https://www.google.com/generate_204"],
        "health_check_enabled": False,
        "health_endpoint": "/eusvc/Health/posNetworkStatus",
        "health_timeout_sec": 0.5,
    }
    base.update(override)
    return base


def test_probe_network_snapshot_server_ok():
    base_url = "https://pos.test"
    http = _FakeHttp(
        {
            ("HEAD", base_url): _Resp(status_code=200),
        }
    )
    service = NetworkProbeService(
        http_client=http,
        endpoint_config_getter=lambda: {"api_base_url": base_url, "request_timeout": 1},
        policy_config_getter=lambda: _policy(),
    )

    result = service.probe_network_snapshot()
    assert result["raw_state"] == NetworkProbeService.RAW_SERVER_OK
    assert result["server_online"] is True
    assert result["internet_online"] is False


def test_probe_network_snapshot_internet_only():
    base_url = "https://pos.test"
    google = "https://www.google.com/generate_204"
    http = _FakeHttp(
        {
            ("HEAD", base_url): requests.ConnectionError("server_down"),
            ("GET", base_url): requests.ConnectionError("server_down"),
            ("HEAD", google): _Resp(status_code=204),
        }
    )
    service = NetworkProbeService(
        http_client=http,
        endpoint_config_getter=lambda: {"api_base_url": base_url, "request_timeout": 1},
        policy_config_getter=lambda: _policy(),
    )

    result = service.probe_network_snapshot()
    assert result["raw_state"] == NetworkProbeService.RAW_INTERNET_ONLY
    assert result["server_online"] is False
    assert result["internet_online"] is True


def test_probe_server_health_capabilities_future_ready():
    base_url = "https://pos.test"
    health_url = "https://pos.test/eusvc/Health/posNetworkStatus"
    payload = {
        "status": 1,
        "data": {
            "capabilities": {
                "ppn_remote_rules": True,
                "login_policy_hint": "server_first",
            }
        },
    }
    http = _FakeHttp(
        {
            ("HEAD", health_url): _Resp(status_code=200),
            ("GET", health_url): _Resp(status_code=200, text=json.dumps(payload)),
        }
    )
    service = NetworkProbeService(
        http_client=http,
        endpoint_config_getter=lambda: {"api_base_url": base_url, "request_timeout": 1},
        policy_config_getter=lambda: _policy(health_check_enabled=True),
    )

    result = service.probe_server()
    assert result["ok"] is True
    assert result["health_supported"] is True
    assert result["health_ok"] is True
    assert result["capabilities"].get("ppn_remote_rules") is True


def test_probe_server_health_404_fallback_to_base_probe():
    base_url = "https://pos.test"
    health_url = "https://pos.test/eusvc/Health/posNetworkStatus"
    http = _FakeHttp(
        {
            ("HEAD", health_url): _Resp(status_code=404),
            ("GET", health_url): _Resp(status_code=404),
            ("HEAD", base_url): _Resp(status_code=200),
        }
    )
    service = NetworkProbeService(
        http_client=http,
        endpoint_config_getter=lambda: {"api_base_url": base_url, "request_timeout": 1},
        policy_config_getter=lambda: _policy(health_check_enabled=True),
    )

    result = service.probe_server()
    assert result["ok"] is True
    assert result["status_code"] == 200
