"""Unit tests for utils/util.py"""

import os
import socket
import pytest
from unittest.mock import patch, MagicMock

_mock_env = MagicMock()
_mock_env.server.socket_dir = ""

with patch("gmi_ieops.config.env", _mock_env):
    from gmi_ieops.utils.util import (
        randstr, arandstr, randint, arandint,
        get_socket_path, get_service_port, APP_ID, _TOKEN_CHARS,
    )


class TestRandom:

    def test_randstr(self):
        assert len(randstr(16)) == 16 and randstr(0) == ""
        assert all(c in _TOKEN_CHARS for c in randstr(100))
        assert len({randstr(16) for _ in range(100)}) > 90

    @pytest.mark.asyncio
    async def test_arandstr(self):
        s = await arandstr(10)
        assert len(s) == 10 and all(c in _TOKEN_CHARS for c in s)

    def test_randint(self):
        for _ in range(100):
            assert 10 <= randint(10, 20) < 20
        assert randint(5, 5) == 5

    @pytest.mark.asyncio
    async def test_arandint(self):
        for _ in range(50):
            assert 0 <= await arandint(0, 100) < 100

    def test_app_id_format(self):
        parts = APP_ID.split(".")
        assert len(parts) >= 2 and len(parts[-1]) == 8


class TestGetSocketPath:

    def test_explicit_dir(self, tmp_path):
        with patch("gmi_ieops.utils.util.APP_ID", "app.abc12345"):
            assert get_socket_path(str(tmp_path)) == os.path.join(str(tmp_path), "app.abc12345.sock")

    def test_empty_uses_cwd(self):
        with patch("gmi_ieops.utils.util.APP_ID", "app.abc12345"):
            assert get_socket_path("") == os.path.join(os.getcwd(), "app.abc12345.sock")


class TestGetServicePort:

    def test_random_port(self):
        port = get_service_port()
        assert isinstance(port, int) and port > 0
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.bind(("127.0.0.1", port))  # still available

    def test_preferred_and_fallback(self):
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.bind(("127.0.0.1", 0)); s.listen(1)
            occupied = s.getsockname()[1]
            assert get_service_port(occupied) != occupied  # falls back

    @pytest.mark.parametrize("val", [0, None])
    def test_zero_or_none_gets_random(self, val):
        assert get_service_port(val) > 0
