"""
Tests for laneswap.utils module.

This module tests environment variable helpers and logging configuration.
"""

import logging
import os

import pytest

from laneswap.utils import get_logger, get_port, get_service_name, setup_logging


class TestGetServiceName:
    """Test get_service_name function."""

    def test_returns_default_when_no_env_or_override(self, clean_env: None) -> None:
        """Test that default service name is returned when no env var or override."""
        result = get_service_name()
        assert result == "service"

    def test_returns_env_var_when_set(self, env_with_service_name: str) -> None:
        """Test that SERVICE_NAME environment variable is used."""
        result = get_service_name()
        assert result == env_with_service_name

    def test_override_takes_precedence_over_env(
        self, env_with_service_name: str
    ) -> None:
        """Test that override parameter takes precedence over env var."""
        override = "custom-service"
        result = get_service_name(override)
        assert result == override
        assert result != env_with_service_name

    def test_accepts_empty_string_override(self, clean_env: None) -> None:
        """Test that empty string override is accepted."""
        result = get_service_name("")
        assert result == ""

    def test_accepts_special_characters(self, clean_env: None) -> None:
        """Test that service names with special characters work."""
        special_name = "my-service_123.test"
        result = get_service_name(special_name)
        assert result == special_name


class TestGetPort:
    """Test get_port function."""

    def test_returns_default_when_no_env_or_override(self, clean_env: None) -> None:
        """Test that default port 5000 is returned when no env var or override."""
        result = get_port()
        assert result == 5000
        assert isinstance(result, int)

    def test_returns_env_var_when_set(self, env_with_port: int) -> None:
        """Test that PORT environment variable is used and converted to int."""
        result = get_port()
        assert result == env_with_port
        assert isinstance(result, int)

    def test_override_takes_precedence_over_env(self, env_with_port: int) -> None:
        """Test that override parameter takes precedence over env var."""
        override = 9000
        result = get_port(override)
        assert result == override
        assert result != env_with_port

    def test_accepts_zero_port(self, clean_env: None) -> None:
        """Test that port 0 (system-assigned) is accepted."""
        result = get_port(0)
        assert result == 0

    def test_accepts_high_port_numbers(self, clean_env: None) -> None:
        """Test that high port numbers (up to 65535) are accepted."""
        result = get_port(65535)
        assert result == 65535

    def test_raises_value_error_on_invalid_env_var(
        self, monkeypatch: pytest.MonkeyPatch
    ) -> None:
        """Test that ValueError is raised when PORT env var is not a valid integer."""
        monkeypatch.setenv("PORT", "not-a-number")
        with pytest.raises(ValueError) as exc_info:
            get_port()
        assert "Invalid PORT environment variable" in str(exc_info.value)
        assert "not-a-number" in str(exc_info.value)

    def test_raises_value_error_on_float_string(
        self, monkeypatch: pytest.MonkeyPatch
    ) -> None:
        """Test that ValueError is raised when PORT is a float string."""
        monkeypatch.setenv("PORT", "5000.5")
        with pytest.raises(ValueError) as exc_info:
            get_port()
        assert "Invalid PORT environment variable" in str(exc_info.value)

    def test_raises_value_error_on_empty_string(
        self, monkeypatch: pytest.MonkeyPatch
    ) -> None:
        """Test that ValueError is raised when PORT is empty string."""
        monkeypatch.setenv("PORT", "")
        with pytest.raises(ValueError):
            get_port()

    def test_parses_string_port_correctly(
        self, monkeypatch: pytest.MonkeyPatch
    ) -> None:
        """Test that string port from env is correctly parsed to int."""
        monkeypatch.setenv("PORT", "8080")
        result = get_port()
        assert result == 8080
        assert isinstance(result, int)


class TestSetupLogging:
    """Test setup_logging function."""

    def test_returns_logger_instance(self, clean_env: None) -> None:
        """Test that setup_logging returns a logger instance."""
        logger = setup_logging()
        assert isinstance(logger, logging.Logger)

    def test_uses_info_level_by_default(self, clean_env: None) -> None:
        """Test that INFO log level is used by default."""
        logger = setup_logging()
        root_logger = logging.getLogger()
        assert root_logger.level == logging.INFO

    def test_uses_env_var_log_level(self, env_with_log_level: str) -> None:
        """Test that LOG_LEVEL environment variable is respected."""
        logger = setup_logging()
        root_logger = logging.getLogger()
        assert root_logger.level == logging.DEBUG

    def test_accepts_level_parameter(self, clean_env: None) -> None:
        """Test that level parameter overrides environment variable."""
        logger = setup_logging(level="WARNING")
        root_logger = logging.getLogger()
        assert root_logger.level == logging.WARNING

    def test_handles_lowercase_level_names(self, clean_env: None) -> None:
        """Test that lowercase log level names are accepted."""
        logger = setup_logging(level="error")
        root_logger = logging.getLogger()
        assert root_logger.level == logging.ERROR

    def test_handles_mixed_case_level_names(self, clean_env: None) -> None:
        """Test that mixed case log level names are accepted."""
        logger = setup_logging(level="WaRnInG")
        root_logger = logging.getLogger()
        assert root_logger.level == logging.WARNING

    def test_all_standard_log_levels(self, clean_env: None) -> None:
        """Test that all standard log levels work correctly."""
        levels = {
            "DEBUG": logging.DEBUG,
            "INFO": logging.INFO,
            "WARNING": logging.WARNING,
            "ERROR": logging.ERROR,
            "CRITICAL": logging.CRITICAL,
        }

        for level_name, level_value in levels.items():
            logger = setup_logging(level=level_name)
            root_logger = logging.getLogger()
            assert root_logger.level == level_value

    def test_falls_back_to_info_on_invalid_level(self, clean_env: None) -> None:
        """Test that invalid log level falls back to INFO."""
        logger = setup_logging(level="INVALID_LEVEL")
        root_logger = logging.getLogger()
        # Invalid level names default to INFO
        assert root_logger.level == logging.INFO

    def test_logger_has_stream_handler(self, clean_env: None) -> None:
        """Test that logger is configured with a stream handler."""
        logger = setup_logging()
        root_logger = logging.getLogger()
        assert len(root_logger.handlers) > 0
        assert any(isinstance(h, logging.StreamHandler) for h in root_logger.handlers)

    def test_logger_output_format(self, clean_env: None, caplog: pytest.LogCaptureFixture) -> None:
        """Test that logger uses the correct output format."""
        logger = setup_logging()
        logger.info("Test message")
        # Check that message was logged
        assert len(caplog.records) > 0

    def test_setup_logging_is_idempotent(self, clean_env: None) -> None:
        """Test that calling setup_logging multiple times doesn't cause issues."""
        logger1 = setup_logging()
        logger2 = setup_logging()
        logger1.info("Test")
        logger2.info("Test")
        # Should not raise any exceptions


class TestGetLogger:
    """Test get_logger function."""

    def test_returns_logger_instance(self) -> None:
        """Test that get_logger returns a logger instance."""
        logger = get_logger()
        assert isinstance(logger, logging.Logger)

    def test_returns_default_name_when_no_arg(self) -> None:
        """Test that default name 'laneswap' is used when no argument."""
        logger = get_logger()
        assert logger.name == "laneswap"

    def test_returns_logger_with_custom_name(self) -> None:
        """Test that custom logger name is used."""
        custom_name = "my-custom-logger"
        logger = get_logger(custom_name)
        assert logger.name == custom_name

    def test_returns_same_logger_for_same_name(self) -> None:
        """Test that same logger instance is returned for same name."""
        logger1 = get_logger("test-logger")
        logger2 = get_logger("test-logger")
        assert logger1 is logger2

    def test_returns_different_loggers_for_different_names(self) -> None:
        """Test that different logger instances are returned for different names."""
        logger1 = get_logger("logger1")
        logger2 = get_logger("logger2")
        assert logger1 is not logger2
        assert logger1.name != logger2.name


class TestIntegration:
    """Integration tests combining multiple utility functions."""

    def test_service_configuration_from_environment(
        self,
        monkeypatch: pytest.MonkeyPatch,
    ) -> None:
        """Test complete service configuration from environment."""
        monkeypatch.setenv("SERVICE_NAME", "integration-test-service")
        monkeypatch.setenv("PORT", "7000")
        monkeypatch.setenv("LOG_LEVEL", "ERROR")

        service_name = get_service_name()
        port = get_port()
        logger = setup_logging()

        assert service_name == "integration-test-service"
        assert port == 7000
        assert logging.getLogger().level == logging.ERROR

    def test_service_configuration_with_overrides(
        self,
        env_with_service_name: str,
        env_with_port: int,
    ) -> None:
        """Test that overrides work correctly when env vars are set."""
        service_name = get_service_name("override-service")
        port = get_port(9999)

        assert service_name == "override-service"
        assert port == 9999

    def test_default_service_configuration(self, clean_env: None) -> None:
        """Test default configuration when no env vars are set."""
        service_name = get_service_name()
        port = get_port()
        logger = setup_logging()

        assert service_name == "service"
        assert port == 5000
        assert isinstance(logger, logging.Logger)
