"""Tests for core library functions."""

import time
from contextlib import redirect_stdout
from io import StringIO
from unittest.mock import patch

import pytest

from augint_library import NetworkError, NetworkTimeoutError, fetch_data, print_hi


class TestPrintHi:
    """Test the print_hi function."""

    def test_print_hi_basic(self) -> None:
        """Test basic greeting functionality."""
        output = StringIO()
        with redirect_stdout(output):
            print_hi("Alice")

        assert output.getvalue() == "Hi Alice\n"

    def test_print_hi_empty_string(self) -> None:
        """Test greeting with empty string."""
        output = StringIO()
        with redirect_stdout(output):
            print_hi("")

        assert output.getvalue() == "Hi \n"

    def test_print_hi_special_characters(self) -> None:
        """Test greeting with special characters."""
        output = StringIO()
        with redirect_stdout(output):
            print_hi("Alice & Bob")

        assert output.getvalue() == "Hi Alice & Bob\n"


class TestFetchData:
    """Test the fetch_data function."""

    def test_fetch_data_success(self) -> None:
        """Test successful data fetch."""
        # Use 0 failure rate to ensure success
        result = fetch_data("/api/test", timeout=2.0, failure_rate=0.0)

        assert result["status"] == "ok"
        assert result["endpoint"] == "/api/test"
        assert "data" in result
        assert "timestamp" in result
        assert isinstance(result["data"]["users"], list)

    def test_fetch_data_timeout(self) -> None:
        """Test timeout handling."""
        # Use very short timeout to trigger timeout
        # Mock random.uniform to always return value > timeout
        with (
            pytest.raises(NetworkTimeoutError) as exc_info,
            patch("augint_library.core.random.uniform", return_value=0.2),
        ):
            fetch_data("/api/test", timeout=0.1, failure_rate=0.0)

        error = exc_info.value
        assert "API endpoint /api/test" in str(error)
        assert error.details["timeout"] == 0.1

    def test_fetch_data_network_error(self) -> None:
        """Test network error handling."""
        # Use 100% failure rate to ensure failure
        # Mock random.uniform to ensure we don't timeout (delay < timeout)
        with (
            pytest.raises(NetworkError) as exc_info,
            patch(
                "augint_library.core.random.uniform", return_value=1.5
            ),  # Less than timeout of 2.0
        ):
            fetch_data("/api/test", timeout=2.0, failure_rate=1.0)

        error = exc_info.value
        assert "Failed to connect to /api/test" in str(error)
        assert error.details["service"] == "API endpoint /api/test"
        assert error.details["status_code"] == 503

    def test_fetch_data_partial_failures(self) -> None:
        """Test that fetch_data sometimes succeeds with partial failure rate."""
        successes = 0
        failures = 0
        attempts = 20

        for _ in range(attempts):
            try:
                fetch_data("/api/test", timeout=2.0, failure_rate=0.5)
                successes += 1
            except (NetworkError, NetworkTimeoutError):
                failures += 1

        # With 50% failure rate, we should see both successes and failures
        assert successes > 0
        assert failures > 0
        assert successes + failures == attempts

    def test_fetch_data_respects_timeout(self) -> None:
        """Test that fetch_data respects the timeout parameter."""
        start_time = time.time()

        # This should complete within timeout
        fetch_data("/api/test", timeout=0.5, failure_rate=0.0)

        elapsed = time.time() - start_time
        assert elapsed < 0.6  # Should be less than timeout + small margin

    @pytest.mark.parametrize(
        "endpoint",
        [
            "/api/users",
            "/api/products",
            "/api/orders/123",
            "/v2/data",
        ],
    )
    def test_fetch_data_different_endpoints(self, endpoint: str) -> None:
        """Test fetch_data with different endpoints."""
        result = fetch_data(endpoint, timeout=2.0, failure_rate=0.0)
        assert result["endpoint"] == endpoint
        assert result["status"] == "ok"
