"""Unit tests for the infrastructure logger module.

Tests setup_logging and get_logger from src/infrastructure/logging/logger.py.
"""

import logging
from pathlib import Path
from unittest.mock import patch

import pytest

from src.infrastructure.logging.logger import get_logger, setup_logging


class TestSetupLogging:
    """Tests for setup_logging function."""

    def test_returns_logger_instance(self, tmp_path):
        """setup_logging returns a logging.Logger."""
        logger = setup_logging(log_dir=tmp_path)
        assert isinstance(logger, logging.Logger)

    def test_logger_name_is_aniworld(self, tmp_path):
        """Returned logger has name 'aniworld'."""
        logger = setup_logging(log_dir=tmp_path)
        assert logger.name == "aniworld"

    def test_creates_log_directory(self, tmp_path):
        """Log directory is created if it does not exist."""
        log_dir = tmp_path / "new_logs"
        setup_logging(log_dir=log_dir)
        assert log_dir.is_dir()

    def test_creates_log_file(self, tmp_path):
        """A log file is created in the log directory."""
        setup_logging(log_dir=tmp_path)
        assert (tmp_path / "fastapi_app.log").exists()

    def test_custom_log_file_name(self, tmp_path):
        """Custom log file name is used."""
        setup_logging(log_file="custom.log", log_dir=tmp_path)
        assert (tmp_path / "custom.log").exists()

    def test_default_log_level_is_info(self, tmp_path):
        """Default log level falls back to INFO."""
        with patch("src.infrastructure.logging.logger.settings") as mock_settings:
            mock_settings.log_level = None
            logger = setup_logging(log_dir=tmp_path)
            assert logger.getEffectiveLevel() == logging.INFO

    def test_custom_log_level_debug(self, tmp_path):
        """Custom log level DEBUG is applied."""
        logger = setup_logging(log_level="DEBUG", log_dir=tmp_path)
        assert logger.getEffectiveLevel() == logging.DEBUG

    def test_custom_log_level_warning(self, tmp_path):
        """Custom log level WARNING is applied."""
        logger = setup_logging(log_level="WARNING", log_dir=tmp_path)
        assert logger.getEffectiveLevel() == logging.WARNING

    def test_log_level_case_insensitive(self, tmp_path):
        """Log level is case-insensitive."""
        logger = setup_logging(log_level="debug", log_dir=tmp_path)
        assert logger.getEffectiveLevel() == logging.DEBUG

    def test_root_logger_has_two_handlers(self, tmp_path):
        """Root logger gets exactly console + file handlers."""
        setup_logging(log_dir=tmp_path)
        root = logging.getLogger()
        # Should be at least 2 handlers (console + file)
        handler_types = {type(h).__name__ for h in root.handlers}
        assert "StreamHandler" in handler_types
        assert "FileHandler" in handler_types

    def test_clears_existing_root_handlers(self, tmp_path):
        """Existing root handlers are cleared to avoid duplicates."""
        root = logging.getLogger()
        root.addHandler(logging.StreamHandler())
        initial_count = len(root.handlers)
        setup_logging(log_dir=tmp_path)
        # After setup, should have exactly 2 (console + file)
        assert len(root.handlers) == 2

    def test_writes_to_log_file(self, tmp_path):
        """Messages are written to the log file."""
        logger = setup_logging(log_dir=tmp_path)
        logger.info("test message for file")
        # Flush handlers
        for handler in logging.getLogger().handlers:
            handler.flush()
        log_content = (tmp_path / "fastapi_app.log").read_text()
        assert "test message for file" in log_content

    def test_log_format_includes_timestamp(self, tmp_path):
        """File log entries include a timestamp."""
        logger = setup_logging(log_dir=tmp_path)
        logger.info("timestamp check")
        for handler in logging.getLogger().handlers:
            handler.flush()
        log_content = (tmp_path / "fastapi_app.log").read_text()
        # Detailed formatter includes date
        assert "-" in log_content  # At minimum YYYY-MM-DD format

    def test_log_format_includes_level_name(self, tmp_path):
        """File log entries include the log level name."""
        logger = setup_logging(log_dir=tmp_path)
        logger.warning("level check")
        for handler in logging.getLogger().handlers:
            handler.flush()
        log_content = (tmp_path / "fastapi_app.log").read_text()
        assert "WARNING" in log_content

    def test_startup_banner_logged(self, tmp_path):
        """Startup banner with log config info is written."""
        setup_logging(log_dir=tmp_path)
        for handler in logging.getLogger().handlers:
            handler.flush()
        log_content = (tmp_path / "fastapi_app.log").read_text()
        assert "Logging configured successfully" in log_content

    def test_level_from_settings(self, tmp_path):
        """Falls back to settings.log_level when no explicit level."""
        with patch("src.infrastructure.logging.logger.settings") as mock_settings:
            mock_settings.log_level = "ERROR"
            logger = setup_logging(log_dir=tmp_path)
            assert logger.getEffectiveLevel() == logging.ERROR

    def test_invalid_log_level_defaults_to_info(self, tmp_path):
        """Invalid log level string falls back to INFO."""
        logger = setup_logging(log_level="INVALID_LEVEL", log_dir=tmp_path)
        assert logger.getEffectiveLevel() == logging.INFO


class TestGetLogger:
    """Tests for get_logger function."""

    def test_returns_logger_with_given_name(self):
        """get_logger returns a logger with the requested name."""
        logger = get_logger("my_module")
        assert logger.name == "my_module"

    def test_returns_same_logger_for_same_name(self):
        """Calling get_logger twice with same name returns same object."""
        a = get_logger("shared_name")
        b = get_logger("shared_name")
        assert a is b

    def test_hierarchical_name(self):
        """Dotted names produce hierarchical loggers."""
        parent = get_logger("app")
        child = get_logger("app.sub")
        assert child.parent is parent or child.parent.name == "app"

    def test_logger_is_standard_logging(self):
        """Returned object is a standard logging.Logger."""
        logger = get_logger("test_std")
        assert isinstance(logger, logging.Logger)
