"""Tests for connector.error_logging."""

import logging
import uuid
from io import StringIO

import pytest
from connector.error_logging import ErrorLogFormatter, ErrorLogRecord, create_error_logger


def _make_error_log_record(**overrides: object) -> ErrorLogRecord:
    defaults: dict[str, object] = {
        "name": "test",
        "level": logging.ERROR,
        "pathname": __file__,
        "lineno": 1,
        "msg": "app/bad_request: oops",
        "args": (),
        "exc_info": None,
    }
    defaults.update(overrides)
    return ErrorLogRecord(**defaults)  # type: ignore[arg-type]


def test_error_log_formatter_appends_details_for_error_log_record():
    formatter = ErrorLogFormatter("%(name)s - %(message)s")
    record = _make_error_log_record()
    record.error_app_id = "my_app"
    record.error_code = "bad_request"
    record.error_fault = "caller"
    record.error_category = "fix_request"
    record.error_retryable = False
    record.error_throttled = False
    record.error_refreshable = False
    record.error_hint = "fix it"

    out = formatter.format(record)

    assert "test - app/bad_request: oops" in out
    assert "Details:" in out
    assert "'error_app_id': 'my_app'" in out
    assert "'error_code': 'bad_request'" in out
    assert "'error_fault': 'caller'" in out
    assert "'error_category': 'fix_request'" in out
    assert "'error_hint': 'fix it'" in out


def test_error_log_formatter_plain_log_record_no_details_block():
    formatter = ErrorLogFormatter("%(name)s - %(message)s")
    record = logging.LogRecord(
        name="plain",
        level=logging.INFO,
        pathname=__file__,
        lineno=1,
        msg="hello",
        args=(),
        exc_info=None,
    )

    out = formatter.format(record)

    assert out == "plain - hello"
    assert "Details:" not in out


def test_error_log_formatter_includes_exception_when_exc_info_set():
    formatter = ErrorLogFormatter("%(name)s - %(message)s")
    try:
        raise RuntimeError("boom")
    except RuntimeError as exc:
        record = _make_error_log_record(
            msg="err",
            exc_info=(type(exc), exc, exc.__traceback__),
        )
        record.error_app_id = "a"
        record.error_code = "internal_error"
        record.error_fault = "unknown"
        record.error_category = "internal"
        record.error_retryable = False
        record.error_throttled = False
        record.error_refreshable = False
        record.error_hint = ""

    out = formatter.format(record)

    assert "RuntimeError: boom" in out
    assert "Traceback" in out
    assert "Details:" in out


@pytest.fixture
def unique_logger_name() -> str:
    return f"connector.tests.error_logging.{uuid.uuid4().hex}"


def test_create_error_logger_sets_level_propagate_and_handler(unique_logger_name: str):
    buf = StringIO()
    log = create_error_logger(unique_logger_name)
    log.handlers.clear()
    handler = logging.StreamHandler(buf)
    handler.setFormatter(ErrorLogFormatter("%(message)s"))
    log.addHandler(handler)

    assert log.name == unique_logger_name
    assert log.level == logging.ERROR
    assert log.propagate is False

    log.handle(
        logging.LogRecord(
            name=log.name,
            level=logging.ERROR,
            pathname=__file__,
            lineno=1,
            msg="x",
            args=(),
            exc_info=None,
        )
    )
    assert buf.getvalue() == "x\n"


def test_create_error_logger_adds_handler_only_once(unique_logger_name: str):
    log1 = create_error_logger(unique_logger_name)
    assert len(log1.handlers) == 1

    log2 = create_error_logger(unique_logger_name)
    assert log2 is log1
    assert len(log2.handlers) == 1
