from ...configurator import config

from pathlib import Path
from os import path as os_path, makedirs
from datetime import datetime
from typing import Any, final



class _Handler:
    __slots__  = ('__enabled',)

    def __init__(
        self, enabled: bool = None
    ):
        if enabled is None:
            enabled = False

        self.__enabled = enabled
        pass


    def enable(self):
        self.__enabled = True
        pass


    def disable(self):
        self.__enabled = False
        pass


    @property
    def enabled(self) -> bool:
        return self.__enabled


    def _flush(self, content: Any) -> None:
        """ This method should be overridden by subclasses. """
        raise NotImplementedError
        pass


    @final
    def flush(self, content: Any = None):
        if self.enabled:
            self._flush(content=content)
        pass

    pass



class Console(_Handler):
    __slots__ = ()

    def __init__(
        self, enabled: bool = None
    ):
        if enabled is None:
            try: enabled = config.logging.outputs.console.enabled
            except: pass

        _Handler.__init__(self, enabled=enabled)
        pass


    def _flush(self, content: Any = None):
        if self.enabled:
            if content is None:
                content = ''

            print(content)
        pass

    pass



class File(_Handler):
    __slots__ = ('__path', '__name')

    def __init__(
        self, enabled: bool = None,
        #
        path: Path|str = None,
        name: str = None,
        bindings: dict[str, Any] = None
    ):
        if enabled is None:
            try: enabled = config.logging.outputs.file.enabled
            except: pass

        if path is None:
            try: path = config.logging.outputs.file.path
            except: pass
            #
        if path is None:
            path = '{app}/logs/{yyyy}-{mm}-{dd}'

        if name is None:
            try: name = config.logging.outputs.file.name
            except: pass
            #
        if name is None:
            name = 'logs_{yyyy}-{mm}-{dd}_{hh}.{mi}.{ss}.{ffffff}.ph'

        if bindings is None:
            bindings = {}

        _Handler.__init__(self, enabled=enabled)

        # custom bindings
        for k, v in bindings.items():
            path = path.replace(f'{{{k}}}', f'{v}')
            name = name.replace(f'{{{k}}}', f'{v}')

        # default bindings
        dt_dict = {
            'yyyy': '%Y',
            'mm': '%m',
            'dd': '%d',
            'hh': '%H',
            'mi': '%M',
            'ss': '%S',
            'ffffff': '%f'
        }

        now = datetime.now()
        for dt_tag, dt_fmt in dt_dict.items():
            path = path.replace(f'{{{dt_tag}}}', now.strftime(dt_fmt))
            name = name.replace(f'{{{dt_tag}}}', now.strftime(dt_fmt))

        self.__path = path.format(app=Path(__file__).parent.parent.parent.parent.parent.parent.parent)
        self.__name = name
        pass


    def _flush(self, content: Any = None):
        if self.enabled:
            if content is None:
                content = ''

            if not os_path.exists(self.__path):
                makedirs(name=self.__path, exist_ok=True)

            try:
                with open(f'{self.__path}/{self.__name}', 'ab') as file:
                    file.write(content.encode(encoding='utf-8'))
            except Exception as exc:
                if "name 'open' is not defined" not in f'{exc}':  # fix: try to ignore exception when flush is called after app destructor
                    raise Exception(exc)

        pass

    pass
