import logging
import os
import sys
from logging.handlers import RotatingFileHandler
from typing import Union


class RollingFileHandler(RotatingFileHandler):

    def __init__(self, filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=False, errors=None) -> None:
        directory: str = os.path.dirname(filename)
        files: list[str] = os.listdir(directory if directory != '' else '.')
        max_files: int = len(files)
        if os.path.isfile(filename):
            os.remove(filename)
        for i in range(max_files):
            if f'{filename}.{i}' in files:
                os.remove(os.path.join(directory, f'{filename}.{i}'))
        super().__init__(
            filename,
            mode=mode,
            maxBytes=maxBytes,
            backupCount=backupCount,
            encoding=encoding,
            delay=delay,
            errors=errors
        )
        self.last_backup_count: int = 0

    # override
    def doRollover(self):
        if self.stream:
            self.stream.close()
            self.stream = None
        # my code starts here
        self.last_backup_count += 1
        next_name: str = "%s.%d" % (self.baseFilename, self.last_backup_count)
        self.rotate(self.baseFilename, next_name)
        # my code ends here
        if not self.delay:
            self.stream = self._open()


class Logger:
    @classmethod
    def init(
            cls,
            name: str = None,
            base_filename: str = None,
            log_format: str = '%(asctime)s - %(name)s - %(levelname)s - %(message)s',
            date_format: str = None,
            level: Union[int, str] = logging.DEBUG,
            max_bytes: int = 10 * 1024 * 1024
    ) -> None:
        logger: logging.Logger = logging.getLogger(name)
        logger.setLevel(level)
        logger.handlers.clear()

        # out handler
        out_handler: logging.StreamHandler = logging.StreamHandler(sys.stdout)
        out_handler.setFormatter(logging.Formatter(log_format, datefmt=date_format))
        out_handler.setLevel(level)
        logger.addHandler(out_handler)

        # err handler
        err_handler: logging.StreamHandler = logging.StreamHandler(sys.stderr)
        err_handler.setFormatter(logging.Formatter(log_format, datefmt=date_format))
        err_handler.setLevel(logging.ERROR)
        logger.addHandler(err_handler)

        # file handler
        if base_filename is not None:
            if os.path.dirname(base_filename) != '':
                os.makedirs(os.path.dirname(base_filename), exist_ok=True)
            file_handler: RollingFileHandler = RollingFileHandler(base_filename, maxBytes=max_bytes)
            file_handler.setFormatter(logging.Formatter(log_format, datefmt=date_format))
            file_handler.setLevel(logging.DEBUG)
            logger.addHandler(file_handler)

    @classmethod
    def debug(cls, message: str, name: str = None, stack_info: bool = False) -> None:
        logging.getLogger(name).debug(message, stack_info=stack_info)

    @classmethod
    def info(cls, message: str, name: str = None, stack_info: bool = False) -> None:
        logging.getLogger(name).info(message, stack_info=stack_info)

    @classmethod
    def warning(cls, message: str, name: str = None, stack_info: bool = True) -> None:
        logging.getLogger(name).warning(message, stack_info=stack_info)

    @classmethod
    def error(cls, message: str, name: str = None, stack_info: bool = True) -> None:
        logging.getLogger(name).error(message, stack_info=stack_info)

    @classmethod
    def critical(cls, message: str, name: str = None, stack_info: bool = True) -> None:
        logging.getLogger(name).critical(message, stack_info=stack_info)

    @classmethod
    def exception(cls, message: str, e: Exception, name: str = None, stack_info: bool = True) -> None:
        logging.getLogger(name).exception(message, exc_info=e, stack_info=stack_info)

    @classmethod
    def shutdown(cls) -> None:
        logging.shutdown()


Logger.init()
