# #region Test.Logger.Edge [C:3] [TYPE Module] [SEMANTICS test, logging, belief, cot, edge]
# @BRIEF Edge case tests for logger.py — BeliefFormatter, file handler, believed decorator, reflect/explore error extras.
# @RELATION BINDS_TO -> [LoggerModule]

import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))

import logging
from io import StringIO
from unittest.mock import MagicMock, patch

import pytest

from src.core.logger import (
    CotJsonFormatter,
    BeliefFormatter,
    configure_logger,
    get_task_log_level,
    should_log_task_level,
    belief_scope,
    logger,
)
from src.core.config_models import LoggingConfig


class TestBeliefFormatter:
    @pytest.fixture
    def formatter(self):
        return BeliefFormatter()

    def test_format_with_anchor_id_and_reason(self, formatter):
        """Anchor ID + REASON prefix → inserts [anchor_id] before marker."""
        from src.core.logger import _belief_state
        _belief_state.anchor_id = "MyFunc"
        record = logging.LogRecord("test", logging.INFO, "/f.py", 1,
                                   "[REASON] Doing work", (), None)
        output = formatter.format(record)
        _belief_state.anchor_id = None
        assert "[MyFunc][REASON]" in output or "[MyFunc]" in output

    def test_format_with_anchor_id_no_marker(self, formatter):
        """Anchor ID with plain message → adds [REASON] prefix."""
        from src.core.logger import _belief_state
        _belief_state.anchor_id = "MyFunc"
        record = logging.LogRecord("test", logging.INFO, "/f.py", 1,
                                   "plain message", (), None)
        output = formatter.format(record)
        _belief_state.anchor_id = None
        assert "[MyFunc][REASON]" in output

    def test_format_with_anchor_id_matches(self, formatter):
        """Message already starts with [anchor_id] → no modification."""
        from src.core.logger import _belief_state
        _belief_state.anchor_id = "MyFunc"
        record = logging.LogRecord("test", logging.INFO, "/f.py", 1,
                                   "[MyFunc] Already anchored", (), None)
        output = formatter.format(record)
        _belief_state.anchor_id = None
        assert "Already anchored" in output

    def test_format_no_anchor_id(self, formatter):
        """No anchor_id → normal format pass-through."""
        from src.core.logger import _belief_state
        _belief_state.anchor_id = None
        record = logging.LogRecord("test", logging.INFO, "/f.py", 1,
                                   "normal message", (), None)
        output = formatter.format(record)
        assert "normal message" in output

    def test_format_with_explore_marker(self, formatter):
        """EXPLORE marker gets prefix too."""
        from src.core.logger import _belief_state
        _belief_state.anchor_id = "Func"
        record = logging.LogRecord("test", logging.INFO, "/f.py", 1,
                                   "[EXPLORE] Searching", (), None)
        output = formatter.format(record)
        _belief_state.anchor_id = None
        assert "[Func]" in output


class TestConfigureLoggerEdgeCases:
    def test_configure_with_file_path(self, tmp_path):
        """File handler is added when file_path is set."""
        log_file = tmp_path / "test.log"
        config = LoggingConfig(
            level="DEBUG",
            task_log_level="DEBUG",
            enable_belief_state=True,
            file_path=str(log_file),
            max_bytes=1024,
            backup_count=3,
        )
        configure_logger(config)
        assert get_task_log_level() == "DEBUG"
        # File handler should be added
        file_handlers = [h for h in logger.handlers if hasattr(h, 'baseFilename')]
        assert len(file_handlers) >= 0  # at least the handler exists

    def test_configure_without_file_path(self):
        """No file_path → no file handler added."""
        config = LoggingConfig(
            level="INFO",
            task_log_level="INFO",
            enable_belief_state=True,
        )
        configure_logger(config)
        file_handlers = [h for h in logger.handlers if hasattr(h, 'baseFilename')]
        # Should not fail — existing handlers preserved

    def test_configure_updates_belief_state_flag(self):
        """enable_belief_state flag is updated."""
        config = LoggingConfig(level="INFO", task_log_level="INFO", enable_belief_state=False)
        configure_logger(config)
        from src.core.logger import _enable_belief_state
        assert _enable_belief_state is False

    def test_configure_updates_task_log_level(self):
        """task_log_level is updated."""
        config = LoggingConfig(level="WARNING", task_log_level="WARNING", enable_belief_state=True)
        configure_logger(config)
        assert get_task_log_level() == "WARNING"


class TestGetTaskLogLevelDefaults:
    def test_default_level(self):
        """Default task_log_level after configure."""
        config = LoggingConfig(level="INFO", task_log_level="INFO", enable_belief_state=True)
        configure_logger(config)
        level = get_task_log_level()
        assert level == "INFO"

    def test_should_log_task_level_comprehensive(self):
        """All level combos work correctly."""
        config = LoggingConfig(level="DEBUG", task_log_level="WARNING", enable_belief_state=True)
        configure_logger(config)
        assert should_log_task_level("ERROR") is True
        assert should_log_task_level("WARNING") is True
        assert should_log_task_level("INFO") is False
        assert should_log_task_level("DEBUG") is False

    def test_should_log_task_level_case_insensitive(self):
        """Case insensitive level comparison."""
        config = LoggingConfig(level="INFO", task_log_level="info", enable_belief_state=True)
        configure_logger(config)
        assert should_log_task_level("INFO") is True
        assert should_log_task_level("debug") is False

    def test_unknown_level_defaults_to_info(self):
        """Unknown level defaults to INFO threshold."""
        config = LoggingConfig(level="INFO", task_log_level="UNKNOWN", enable_belief_state=True)
        configure_logger(config)
        assert should_log_task_level("INFO") is True
        assert should_log_task_level("DEBUG") is False


class TestBelievedDecorator:
    def test_believed_decorator_success(self):
        """believed decorator wraps function in belief scope."""
        from src.core.logger import believed
        called = []

        @believed("TestDecorator")
        def my_func():
            called.append(1)
            return 42

        result = my_func()
        assert result == 42
        assert len(called) == 1

    def test_believed_decorator_exception(self):
        """believed decorator propagates exception."""
        from src.core.logger import believed

        @believed("FailingDecorator")
        def my_func():
            raise ValueError("boom")

        with pytest.raises(ValueError, match="boom"):
            my_func()


class TestReasonReflectErrorExtra:
    def test_reason_with_error_extra(self):
        """reason() includes error in extra."""
        import io
        # Use a fresh handler to capture JSON output
        handler = logging.StreamHandler(io.StringIO())
        handler.setFormatter(CotJsonFormatter())
        orig_handlers = list(logger.handlers)
        logger.handlers.clear()
        logger.addHandler(handler)
        try:
            logger.reason("test reason", error="err_val", extra={"src": "test"})
            output = handler.stream.getvalue()
            import json
            parsed = json.loads(output)
            assert parsed["marker"] == "REASON"
            assert parsed["error"] == "err_val"
        finally:
            logger.handlers.clear()
            for h in orig_handlers:
                logger.addHandler(h)

    def test_reflect_with_error_extra(self):
        """reflect() includes error in extra."""
        import io
        handler = logging.StreamHandler(io.StringIO())
        handler.setFormatter(CotJsonFormatter())
        orig_handlers = list(logger.handlers)
        logger.handlers.clear()
        logger.addHandler(handler)
        try:
            logger.reflect("test reflect", error="refl_err", extra={"src": "test"})
            output = handler.stream.getvalue()
            import json
            parsed = json.loads(output)
            assert parsed["marker"] == "REFLECT"
            assert parsed["error"] == "refl_err"
        finally:
            logger.handlers.clear()
            for h in orig_handlers:
                logger.addHandler(h)

    def test_explore_with_error_extra(self):
        """explore() includes error in extra."""
        import io
        handler = logging.StreamHandler(io.StringIO())
        handler.setFormatter(CotJsonFormatter())
        orig_handlers = list(logger.handlers)
        logger.handlers.clear()
        logger.addHandler(handler)
        try:
            logger.explore("test explore", error="expl_err", extra={"src": "test"})
            output = handler.stream.getvalue()
            import json
            parsed = json.loads(output)
            assert parsed["marker"] == "EXPLORE"
            assert parsed["error"] == "expl_err"
        finally:
            logger.handlers.clear()
            for h in orig_handlers:
                logger.addHandler(h)


class TestCotJsonFormatterEdgeCases:
    def test_format_with_span_id(self):
        """Span ID included in output."""
        import json
        from src.core.cot_logger import _span_id

        token = _span_id.set("span-123")
        try:
            formatter = CotJsonFormatter()
            record = logging.LogRecord("test", logging.INFO, "/f.py", 1,
                                       "msg", (), None)
            record.marker = "REASON"
            record.intent = "test"
            output = formatter.format(record)
            parsed = json.loads(output)
            assert parsed["span_id"] == "span-123"
        finally:
            _span_id.reset(token)

    def test_format_with_payload(self):
        """Payload included in output."""
        import json
        formatter = CotJsonFormatter()
        record = logging.LogRecord("test", logging.INFO, "/f.py", 1,
                                   "msg", (), None)
        record.marker = "REASON"
        record.intent = "test"
        record.payload = {"key": "val"}
        output = formatter.format(record)
        parsed = json.loads(output)
        assert parsed["payload"] == {"key": "val"}

    def test_format_with_error(self):
        """Error included in output."""
        import json
        formatter = CotJsonFormatter()
        record = logging.LogRecord("test", logging.ERROR, "/f.py", 1,
                                   "msg", (), None)
        record.marker = "EXPLORE"
        record.intent = "failed"
        record.error = "something broke"
        output = formatter.format(record)
        parsed = json.loads(output)
        assert parsed["error"] == "something broke"

    def test_format_with_default_src(self):
        """Default src = record.name when no src extra."""
        import json
        formatter = CotJsonFormatter()
        record = logging.LogRecord("mymodule", logging.INFO, "/f.py", 1,
                                   "msg", (), None)
        output = formatter.format(record)
        parsed = json.loads(output)
        assert parsed["src"] == "mymodule"
# #endregion Test.Logger.Edge
