import json
import logging
from unittest import mock

from google.api_core.client_logging import (
    setup_logging,
    initialize_logging,
    StructuredLogFormatter,
)


def reset_logger(scope):
    logger = logging.getLogger(scope)
    logger.handlers = []
    logger.setLevel(logging.NOTSET)
    logger.propagate = True


def test_setup_logging_w_no_scopes():
    with mock.patch("google.api_core.client_logging._BASE_LOGGER_NAME", "foogle"):
        setup_logging()
        base_logger = logging.getLogger("foogle")
        assert base_logger.handlers == []
        assert not base_logger.propagate
        assert base_logger.level == logging.NOTSET

    reset_logger("foogle")


def test_setup_logging_w_base_scope():
    with mock.patch("google.api_core.client_logging._BASE_LOGGER_NAME", "foogle"):
        setup_logging("foogle")
        base_logger = logging.getLogger("foogle")
        assert isinstance(base_logger.handlers[0], logging.StreamHandler)
        assert not base_logger.propagate
        assert base_logger.level == logging.DEBUG

    reset_logger("foogle")


def test_setup_logging_w_configured_scope():
    with mock.patch("google.api_core.client_logging._BASE_LOGGER_NAME", "foogle"):
        base_logger = logging.getLogger("foogle")
        base_logger.propagate = False
        setup_logging("foogle")
        assert base_logger.handlers == []
        assert not base_logger.propagate
        assert base_logger.level == logging.NOTSET

    reset_logger("foogle")


def test_setup_logging_w_module_scope():
    with mock.patch("google.api_core.client_logging._BASE_LOGGER_NAME", "foogle"):
        setup_logging("foogle.bar")

        base_logger = logging.getLogger("foogle")
        assert base_logger.handlers == []
        assert not base_logger.propagate
        assert base_logger.level == logging.NOTSET

        module_logger = logging.getLogger("foogle.bar")
        assert isinstance(module_logger.handlers[0], logging.StreamHandler)
        assert not module_logger.propagate
        assert module_logger.level == logging.DEBUG

    reset_logger("foogle")
    reset_logger("foogle.bar")


def test_setup_logging_w_incorrect_scope():
    with mock.patch("google.api_core.client_logging._BASE_LOGGER_NAME", "foogle"):
        setup_logging("abc")

        base_logger = logging.getLogger("foogle")
        assert base_logger.handlers == []
        assert not base_logger.propagate
        assert base_logger.level == logging.NOTSET

        # TODO(https://github.com/googleapis/python-api-core/issues/759): update test once we add logic to ignore an incorrect scope.
        logger = logging.getLogger("abc")
        assert isinstance(logger.handlers[0], logging.StreamHandler)
        assert not logger.propagate
        assert logger.level == logging.DEBUG

    reset_logger("foogle")
    reset_logger("abc")


def test_initialize_logging():

    with mock.patch("os.getenv", return_value="foogle.bar"):
        with mock.patch("google.api_core.client_logging._BASE_LOGGER_NAME", "foogle"):
            initialize_logging()

            base_logger = logging.getLogger("foogle")
            assert base_logger.handlers == []
            assert not base_logger.propagate
            assert base_logger.level == logging.NOTSET

            module_logger = logging.getLogger("foogle.bar")
            assert isinstance(module_logger.handlers[0], logging.StreamHandler)
            assert not module_logger.propagate
            assert module_logger.level == logging.DEBUG

            # Check that `initialize_logging()` is a no-op after the first time by verifying that user-set configs are not modified:
            base_logger.propagate = True
            module_logger.propagate = True

            initialize_logging()

            assert base_logger.propagate
            assert module_logger.propagate

    reset_logger("foogle")
    reset_logger("foogle.bar")


def test_structured_log_formatter():
    # TODO(https://github.com/googleapis/python-api-core/issues/761): Test additional fields when implemented.
    record = logging.LogRecord(
        name="Appelation",
        level=logging.DEBUG,
        msg="This is a test message.",
        pathname="some/path",
        lineno=25,
        args=None,
        exc_info=None,
    )

    # Extra fields:
    record.rpcName = "bar"

    formatted_msg = StructuredLogFormatter().format(record)
    parsed_msg = json.loads(formatted_msg)

    assert parsed_msg["name"] == "Appelation"
    assert parsed_msg["severity"] == "DEBUG"
    assert parsed_msg["message"] == "This is a test message."
    assert parsed_msg["rpcName"] == "bar"
