# ruff: noqa: F401
"""GMSO: General Molecular Simulation Object."""

import logging
import sys
from logging.handlers import RotatingFileHandler

from .core.angle import Angle
from .core.angle_type import AngleType
from .core.atom import Atom
from .core.atom_type import AtomType
from .core.bond import Bond
from .core.bond_type import BondType
from .core.box import Box
from .core.dihedral import Dihedral
from .core.dihedral_type import DihedralType
from .core.element import Element
from .core.forcefield import ForceField
from .core.improper import Improper
from .core.improper_type import ImproperType
from .core.pairpotential_type import PairPotentialType
from .core.topology import Topology
from .core.virtual_site import VirtualSite
from .core.virtual_type import VirtualType

__version__ = "0.17.0"


class DeduplicationFilter(logging.Filter):
    """A logging filter that suppresses duplicate messages."""

    def __init__(self):
        super().__init__()
        self.logged_messages = set()

    def filter(self, record):
        log_entry = (record.name, record.levelno, record.msg)
        if log_entry not in self.logged_messages:
            self.logged_messages.add(log_entry)
            return True
        return False


class HeaderRotatingFileHandler(RotatingFileHandler):
    def __init__(
        self,
        filename,
        mode="w",
        maxBytes=0,
        backupCount=0,
        encoding=None,
        delay=False,
        header="",
    ):
        self.header = header
        super().__init__(filename, mode, maxBytes, backupCount, encoding, delay)

    def _open(self):
        """
        Open the current base log file, with the header written.
        """
        stream = super()._open()
        if stream.tell() == 0 and self.header:  # Only write header if file is empty
            stream.write(self.header + "\n")
        return stream


class GMSOLogger:
    def __init__(self):
        self.library_logger = logging.getLogger("gmso")
        self.library_logger.setLevel(logging.DEBUG)

        # Create handlers
        self.console_handler = logging.StreamHandler(sys.stdout)
        self.console_handler.setLevel(logging.WARNING)

        # Create a formatter
        self.formatter = logging.Formatter(
            "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
        )

        # Add formatter to handlers
        self.console_handler.setFormatter(self.formatter)

        # Initialize and add the deduplication filter
        self.dedup_filter = DeduplicationFilter()
        self.console_handler.addFilter(self.dedup_filter)

        # Clear any previous handlers to avoid duplicates in Jupyter
        self._clear_handlers()

        # Add handlers to the library logger
        self.library_logger.addHandler(self.console_handler)

    def _clear_handlers(self):
        handlers = self.library_logger.handlers[:]
        for handler in handlers:
            self.library_logger.removeHandler(handler)

    def debug_file(self, filename: str):
        """Print logging Debug messages to file `filename`."""
        # Get the path to the Python interpreter
        python_executable = sys.executable

        # Get the list of command-line arguments
        command_arguments = sys.argv

        # Construct the full command
        full_command = [python_executable] + command_arguments
        header = f"Log details for GMSO {__version__} from running \n{full_command}"
        self.file_handler = HeaderRotatingFileHandler(
            filename, mode="a", maxBytes=10**6, backupCount=2, header=header
        )
        self.file_handler.setLevel(logging.DEBUG)

        self.file_handler.addFilter(DeduplicationFilter())  # fresh duplication handler
        self.file_handler.setFormatter(self.formatter)
        self.library_logger.addHandler(self.file_handler)

    def print_level(self, level: str):
        """Print sys.stdout screen based on the logging `level` passed."""
        levelDict = {
            "notset": logging.NOTSET,
            "debug": logging.DEBUG,
            "info": logging.INFO,
            "warning": logging.WARNING,
            "error": logging.ERROR,
            "critical": logging.CRITICAL,
        }
        logLevel = levelDict.get(level.lower())
        if logLevel:
            self.console_handler.setLevel(logLevel)  # sets stdout
        else:
            raise ValueError(
                f"INCORRECT {level=}. Please set level of {levelDict.keys()}"
            )


# Example usage in __init__.py
gmso_logger = GMSOLogger()
gmso_logger.library_logger.setLevel(logging.WARNING)
