import logging
import os
import tempfile
import unittest

from xsdk.xlog import DEFAULT_PYTHON_LOGGER_NAME, DEFAULT_XLOG_NAME, Xlog, XlogConfig


class XlogTestCase(unittest.TestCase):
    def setUp(self) -> None:
        self._reset_loggers()

    def tearDown(self) -> None:
        self._reset_loggers()

    def _reset_loggers(self) -> None:
        Xlog.logger = None
        for logger_name in (DEFAULT_XLOG_NAME, DEFAULT_PYTHON_LOGGER_NAME):
            logger = logging.getLogger(logger_name)
            for handler in list(logger.handlers):
                logger.removeHandler(handler)
                handler.close()
            logger.setLevel(logging.NOTSET)
            logger.propagate = True

    def test_get_logger_without_args_uses_default_config(self) -> None:
        logger = Xlog.get_logger()

        self.assertIs(logger, Xlog.get_logger())
        self.assertEqual(logger.log_conf.log_level, logging.DEBUG)
        self.assertTrue(logger.log_conf.console_log_flag)
        self.assertFalse(logger.log_conf.file_log_flag)

    def test_get_logger_supports_config_path_keyword(self) -> None:
        with tempfile.TemporaryDirectory() as tmpdir:
            log_dir = os.path.join(tmpdir, "logs")
            conf_file = os.path.join(tmpdir, "sys.ini")
            conf_text = "\n".join([
                "[xlog]",
                "log_level=WARNING",
                "console_log_flag=N",
                "file_log_flag=Y",
                f"file_log_path={log_dir}",
                "file_log_name=app.log",
                "",
            ])
            with open(conf_file, "w", encoding="utf-8") as file_obj:
                file_obj.write(conf_text)

            logger = Xlog.get_logger(config_path=conf_file)

            self.assertEqual(logger.log_conf.log_level, logging.WARNING)
            self.assertFalse(logger.log_conf.console_log_flag)
            self.assertTrue(logger.log_conf.file_log_flag)
            self.assertEqual(logger.log_conf.file_log_path, log_dir)
            self.assertEqual(logger.log_conf.file_log_name, "app.log")
            self.assertTrue(os.path.isdir(log_dir))

    def test_set_level_updates_logger_and_handlers(self) -> None:
        logger = Xlog.get_logger(config_dict={"log_level": "ERROR", "console_log_flag": "Y"})

        self.assertEqual(logger._logger.level, logging.ERROR)
        self.assertTrue(all(handler.level == logging.ERROR for handler in logger._logger.handlers))

        logger.set_level("DEBUG")

        self.assertEqual(logger._logger.level, logging.DEBUG)
        self.assertTrue(all(handler.level == logging.DEBUG for handler in logger._logger.handlers))

    def test_reinitializing_xlog_does_not_duplicate_handlers(self) -> None:
        logger1 = Xlog({"log_level": "INFO", "console_log_flag": "Y"})
        handler_count1 = len(logger1._logger.handlers)

        logger2 = Xlog({"log_level": "INFO", "console_log_flag": "Y"})

        self.assertEqual(handler_count1, 1)
        self.assertEqual(len(logger2._logger.handlers), 1)

    def test_file_logging_writes_to_expected_file(self) -> None:
        with tempfile.TemporaryDirectory() as tmpdir:
            logger = Xlog({
                "log_level": "INFO",
                "console_log_flag": "N",
                "file_log_flag": "Y",
                "file_log_path": tmpdir,
                "file_log_name": "app.log",
            })

            logger.info("file log message")
            for handler in logger._logger.handlers:
                handler.flush()

            log_file = os.path.join(tmpdir, "app.log")
            self.assertTrue(os.path.isfile(log_file))
            with open(log_file, "r", encoding="utf-8") as file_obj:
                content = file_obj.read()

        self.assertIn("file log message", content)

    def test_get_default_logger_sets_level_and_handler(self) -> None:
        logger = Xlog.get_default_logger("ERROR")

        self.assertEqual(logger.level, logging.ERROR)
        self.assertTrue(logger.handlers)
        self.assertTrue(all(handler.level == logging.ERROR for handler in logger.handlers))

        logger = Xlog.get_default_logger("DEBUG")

        self.assertEqual(logger.level, logging.DEBUG)
        self.assertTrue(all(handler.level == logging.DEBUG for handler in logger.handlers))

    def test_get_stack_level_accepts_multiple_keyword_names(self) -> None:
        kwargs = {"stacklevel": "3"}
        self.assertEqual(Xlog._get_stack_level(kwargs), 3)
        self.assertEqual(kwargs, {})

        kwargs = {"stack_level": "4"}
        self.assertEqual(Xlog._get_stack_level(kwargs), 4)
        self.assertEqual(kwargs, {})

        kwargs = {"stacklevel": "bad"}
        self.assertEqual(Xlog._get_stack_level(kwargs), 2)

    def test_parse_dict_normalizes_values(self) -> None:
        with tempfile.TemporaryDirectory() as tmpdir:
            config = XlogConfig.parse_dict({
                "log_level": "20",
                "console_log_flag": "false",
                "file_log_flag": "yes",
                "file_log_path": tmpdir,
                "file_log_name": "",
            })

        self.assertEqual(config.log_level, logging.INFO)
        self.assertFalse(config.console_log_flag)
        self.assertTrue(config.file_log_flag)
        self.assertEqual(config.file_log_path, tmpdir)
        self.assertEqual(config.file_log_name, "xlog.log")


if __name__ == "__main__":
    unittest.main()
