# coding: utf-8

from typing import Any
from time import monotonic, ctime
from datetime import date
from typing import TextIO, Optional
from threading import RLock
from os.path import join as p_join, abspath, split as p_split

from . import AbstractLogBase

TIME_TO_FLUSH: int = 120    # Время по умолчанию с момента прошлого сброса лога,
                            # после которого, выполняется принудительный сброс буферов


class AbstractFileLog(AbstractLogBase):
    def __init__(self, prefix: str = 'main'):
        super().__init__(prefix)
        self.fd: Optional[TextIO] = None
        self.lock = RLock()

    def _write(self, mark: str, msg: Any):
        if self.fd is None:
            raise ValueError(f'Не определён файл логирования')

        self.lock.acquire()
        try:

            tm = ctime()
            for l in super()._write_helper(mark, msg):
                self.fd.write(f'{tm} | {l}')

        finally:
            self.lock.release()


class FileLog(AbstractFileLog):
    @staticmethod
    def _open_file(file_name: str):
        return open(file_name, 'a', encoding='utf-8')

    def __init__(self, prefix: str = 'main',
                 file_name: Optional[str] = None,
                 file_obj: Optional[TextIO] = None,
                 time_to_flush: int = TIME_TO_FLUSH
                 ):

        super().__init__(prefix=prefix)
        self.fd: Optional[TextIO] = None

        self.flush_time = monotonic()
        self.time_to_flush = time_to_flush      # Время с момента прошлого сброса лога,
                                                # после которого, выполняется принудительный сброс буферов

        if file_name is not None:
            self.fd = self._open_file(file_name)

        else:
            self.fd = file_obj

        if self.fd is None:
            raise ValueError('Не задан файл для записи журналов')

    def flush(self, time_mark: float = None):
        if time_mark is None:
            time_mark = monotonic()

        self.flush_time = time_mark
        self.fd.flush()

    def close(self):
        self.fd.flush()
        self.fd.close()
        self.fd = None

    def __del__(self):
        if self.fd is not None:
            self.fd.flush()
            self.fd.close()
            self.fd = None

    def _flush_event(self):
        t = monotonic()
        if t - self.flush_time >= self.time_to_flush:
            self.flush(t)

    def _write(self, mark: str, msg: Any):
        super()._write(mark=mark, msg=msg)

        self._flush_event()

    def sub_log(self, name: str):
        if self.fd is None:
            raise ValueError('Попытка использовать закрытый файл журнала')

        return self.__class__(f'{self.prefix}/{name}', file_obj=self.fd, time_to_flush=self.time_to_flush)


class LogrotateFile(FileLog):
    def __init__(self,
                 file_base: str,
                 prefix: str = 'main',
                 file_obj: Optional[TextIO] = None,
                 time_to_flush: int = TIME_TO_FLUSH):

        d = date.today()
        if file_obj is None:
            super().__init__(prefix=prefix, file_name=f'{file_base}-{d}.log', time_to_flush=time_to_flush)
        else:
            super().__init__(prefix=prefix, file_obj=file_obj, time_to_flush=time_to_flush)

        self.logrotate_base = file_base
        self.logrotate_date = d

    @classmethod
    def make(cls, directory: str = '.', prefix: str = 'main', time_to_flush: int = TIME_TO_FLUSH):
        if len(p_split(prefix)) > 1:
            raise ValueError(f'Префикс журналирования не должен быть похож '
                             f'на пусть файловой системы, убери знак разделения директорий: {prefix}')

        directory = abspath(directory)
        file_base = p_join(directory, prefix)
        return cls(prefix=prefix, file_base=file_base, time_to_flush=time_to_flush)

    def flush(self, time_mark: float = None, no_rotate: bool = False):
        if not no_rotate:
            d = date.today()
            if self.logrotate_date != d:
                self.logrotate_rotate()
                return

        super().flush(time_mark=time_mark)

    def logrotate_rotate(self):
        d = date.today()
        file_name = f'{self.logrotate_base}-{d}.log'
        _fd = self.fd
        self.fd = self._open_file(file_name)
        self.logrotate_date = d
        self.flush(no_rotate=True)
        _fd.flush()
        _fd.close()

    def sub_log(self, name: str):
        return self.__class__(
            file_base=self.logrotate_base,
            file_obj=self.fd, prefix=f'{self.prefix}/{name}',
            time_to_flush=self.time_to_flush
        )
