"""
Unit tests for ProxyManager.

Tests proxy configuration from environment and config.
"""

from __future__ import annotations

import pytest
import os
from unittest.mock import Mock, patch

from langbot.pkg.utils.proxy import ProxyManager


pytestmark = pytest.mark.asyncio


class TestProxyManager:
    """Tests for ProxyManager class."""

    def _create_mock_app(self, proxy_config: dict = None):
        """Create mock app with proxy config."""
        mock_app = Mock()
        mock_app.instance_config = Mock()
        mock_app.instance_config.data = {'proxy': proxy_config or {}}
        return mock_app

    def test_init_creates_empty_proxies(self):
        """ProxyManager initializes with empty forward_proxies."""
        mock_app = self._create_mock_app()
        pm = ProxyManager(mock_app)

        assert pm.forward_proxies == {}

    async def test_initialize_reads_env_variables(self):
        """initialize reads HTTP_PROXY from environment."""
        mock_app = self._create_mock_app()

        with patch.dict(os.environ, {'HTTP_PROXY': 'http://env-proxy:8080', 'HTTPS_PROXY': 'https://env-proxy:8443'}):
            pm = ProxyManager(mock_app)
            await pm.initialize()

            assert pm.forward_proxies['http://'] == 'http://env-proxy:8080'
            assert pm.forward_proxies['https://'] == 'https://env-proxy:8443'

    async def test_initialize_reads_lower_case_env(self):
        """initialize reads lower case http_proxy from environment."""
        mock_app = self._create_mock_app()

        with patch.dict(os.environ, {'http_proxy': 'http://lower-proxy:8080'}, clear=True):
            # Clear HTTP_PROXY to test fallback
            if 'HTTP_PROXY' in os.environ:
                del os.environ['HTTP_PROXY']

            pm = ProxyManager(mock_app)
            await pm.initialize()

            assert pm.forward_proxies['http://'] == 'http://lower-proxy:8080'

    async def test_initialize_config_overrides_env(self):
        """Config proxy overrides environment variables."""
        mock_app = self._create_mock_app(
            proxy_config={
                'http': 'http://config-proxy:8080',
                'https': 'https://config-proxy:8443',
            }
        )

        with patch.dict(os.environ, {'HTTP_PROXY': 'http://env-proxy:8080'}):
            pm = ProxyManager(mock_app)
            await pm.initialize()

            assert pm.forward_proxies['http://'] == 'http://config-proxy:8080'
            assert pm.forward_proxies['https://'] == 'https://config-proxy:8443'

    async def test_initialize_sets_env_variables(self):
        """initialize sets proxy to environment variables."""
        mock_app = self._create_mock_app(
            proxy_config={
                'http': 'http://test-proxy:8080',
                'https': 'https://test-proxy:8443',
            }
        )

        pm = ProxyManager(mock_app)
        await pm.initialize()

        assert os.environ.get('HTTP_PROXY') == 'http://test-proxy:8080'
        assert os.environ.get('HTTPS_PROXY') == 'https://test-proxy:8443'

    async def test_initialize_handles_empty_config(self):
        """initialize handles empty proxy config."""
        mock_app = self._create_mock_app(proxy_config={})

        with patch.dict(os.environ, clear=True):
            pm = ProxyManager(mock_app)
            await pm.initialize()

            assert pm.forward_proxies['http://'] is None
            assert pm.forward_proxies['https://'] is None

    async def test_initialize_handles_no_env_no_config(self):
        """initialize handles no env and no config."""
        mock_app = self._create_mock_app(proxy_config={})

        # Clear proxy env vars
        env_backup = {}
        for key in ['HTTP_PROXY', 'http_proxy', 'HTTPS_PROXY', 'https_proxy']:
            env_backup[key] = os.environ.get(key)
            if key in os.environ:
                del os.environ[key]

        try:
            pm = ProxyManager(mock_app)
            await pm.initialize()

            assert pm.forward_proxies['http://'] is None
            assert pm.forward_proxies['https://'] is None
        finally:
            # Restore env
            for key, value in env_backup.items():
                if value is not None:
                    os.environ[key] = value

    def test_get_forward_proxies_returns_copy(self):
        """get_forward_proxies returns a copy of the dict."""
        mock_app = self._create_mock_app()
        pm = ProxyManager(mock_app)
        pm.forward_proxies = {'http://': 'http://test:8080'}

        result = pm.get_forward_proxies()

        assert result == pm.forward_proxies
        assert result is not pm.forward_proxies  # Different object

    def test_get_forward_proxies_modification_safe(self):
        """Modifying returned dict doesn't affect internal state."""
        mock_app = self._create_mock_app()
        pm = ProxyManager(mock_app)
        pm.forward_proxies = {'http://': 'http://test:8080'}

        result = pm.get_forward_proxies()
        result['http://'] = 'http://modified:9999'

        assert pm.forward_proxies['http://'] == 'http://test:8080'

    async def test_initialize_http_only_config(self):
        """initialize handles http-only config."""
        mock_app = self._create_mock_app(
            proxy_config={
                'http': 'http://http-only:8080',
            }
        )

        # Clear any existing proxy env vars
        env_backup = {}
        for key in ['HTTP_PROXY', 'http_proxy', 'HTTPS_PROXY', 'https_proxy']:
            env_backup[key] = os.environ.get(key)
            if key in os.environ:
                del os.environ[key]

        try:
            pm = ProxyManager(mock_app)
            await pm.initialize()

            assert pm.forward_proxies['http://'] == 'http://http-only:8080'
            assert pm.forward_proxies['https://'] is None
        finally:
            # Restore env
            for key, value in env_backup.items():
                if value is not None:
                    os.environ[key] = value
