"""Unit tests for IndieFlags client. Mock HTTP (urllib)."""
import json
from unittest.mock import MagicMock, patch

import pytest

from indie_flags import IndieFlags


def _make_response(body: dict):
    data = json.dumps(body).encode()
    resp = MagicMock()
    resp.read.return_value = data
    resp.__enter__ = MagicMock(return_value=resp)
    resp.__exit__ = MagicMock(return_value=None)
    return resp


@patch("indie_flags.client.urllib.request.urlopen")
def test_init_sends_api_key_and_env_in_headers_and_query(mock_urlopen):
    mock_urlopen.return_value = _make_response({"flags": {"my-flag": True}})
    client = IndieFlags(
        api_key="test-key",
        base_url="https://api.example.com",
        env="staging",
    )
    client.init()
    assert mock_urlopen.called
    call_args = mock_urlopen.call_args
    req = call_args[0][0]
    assert req.headers.get("X-Api-Key") == "test-key"
    assert req.headers.get("X-Environment") == "staging"
    url = getattr(req, "full_url", req.selector or req.get_full_url() if hasattr(req, "get_full_url") else str(req))
    assert "eval/flags" in url
    assert "env=staging" in url
    assert "apiKey=test-key" in url


@patch("indie_flags.client.urllib.request.urlopen")
def test_init_sends_email_when_provided(mock_urlopen):
    mock_urlopen.return_value = _make_response({"flags": {}})
    client = IndieFlags(
        api_key="key",
        base_url="https://api.example.com",
        email="u@example.com",
    )
    client.init()
    req = mock_urlopen.call_args[0][0]
    assert req.headers.get("X-Flag-Email") == "u@example.com"
    url = getattr(req, "full_url", "")
    assert "email=" in url


@patch("indie_flags.client.urllib.request.urlopen")
def test_init_returns_flags_and_caches(mock_urlopen):
    mock_urlopen.return_value = _make_response({"flags": {"on": True, "theme": "dark"}})
    client = IndieFlags(api_key="k", base_url="https://api.example.com", cache_ttl_sec=60)
    flags = client.init()
    assert flags == {"on": True, "theme": "dark"}
    assert client.is_enabled("on") is True
    assert client.get_value("theme") == "dark"
    # second call uses cache
    assert mock_urlopen.call_count == 1


@patch("indie_flags.client.urllib.request.urlopen")
def test_get_value_fetches_single_flag_when_not_cached(mock_urlopen):
    mock_urlopen.return_value = _make_response({"enabled": True, "flag": "my-flag"})
    client = IndieFlags(api_key="k", base_url="https://api.example.com", cache_ttl_sec=0)
    assert client.get_value("my_flag") is True
    assert mock_urlopen.called
    req = mock_urlopen.call_args[0][0]
    url = getattr(req, "full_url", "")
    assert "/eval" in url and "?" in url
    assert "flag=my-flag" in url


@patch("indie_flags.client.urllib.request.urlopen")
def test_get_value_returns_string_for_value_field(mock_urlopen):
    mock_urlopen.return_value = _make_response({"value": "blue", "flag": "color"})
    client = IndieFlags(api_key="k", base_url="https://api.example.com", cache_ttl_sec=0)
    assert client.get_value("color") == "blue"


@patch("indie_flags.client.urllib.request.urlopen")
def test_is_enabled_returns_false_when_get_value_not_true(mock_urlopen):
    mock_urlopen.return_value = _make_response({"enabled": False})
    client = IndieFlags(api_key="k", base_url="https://api.example.com", cache_ttl_sec=0)
    assert client.is_enabled("x") is False


def test_normalize_key_lowercase_and_underscores_to_dashes():
    client = IndieFlags(api_key="k", cache_ttl_sec=0)
    assert client._normalize_key("  My_Flag  ") == "my-flag"


def test_empty_api_key_raises():
    with pytest.raises(ValueError, match="api_key is required"):
        IndieFlags(api_key="   ")


@patch("indie_flags.client.urllib.request.urlopen")
def test_invalidate_cache_clears_cache(mock_urlopen):
    mock_urlopen.return_value = _make_response({"flags": {"a": True}})
    client = IndieFlags(api_key="k", base_url="https://api.example.com", cache_ttl_sec=60)
    client.init()
    client.invalidate_cache()
    mock_urlopen.reset_mock()
    mock_urlopen.return_value = _make_response({"enabled": False})
    assert client.get_value("b") is False
    assert mock_urlopen.called
