import re
import logging
from datetime import datetime, timezone, timedelta
from typing import Optional


# Необязательный импорт dglog
try:
    import dglog
    LoggerType = logging.Logger | dglog.Logger
    DG_LOG_READY = True
except ImportError:
    LoggerType = logging.Logger
    DG_LOG_READY = False


class TimerEntry:
    def __init__(self, name: Optional[str] = None, desc: Optional[str] = None):
        self.name = name
        self.desc = desc
        self._start: datetime = datetime.now(timezone.utc)
        self._stop: Optional[datetime] = None

    def start(self, desc: Optional[str] = None):
        self._start = datetime.now(timezone.utc)
        self.desc = desc or self.desc

    def stop(self) -> timedelta:
        self._stop = datetime.now(timezone.utc)
        return self._stop - self._start

class Timer:
    def __init__(self, logger: Optional[LoggerType] = None, desc: Optional[str] = None):
        self.timers: dict[str, TimerEntry] = {}
        self.logger = logger or (dglog.get_logger() if DG_LOG_READY else logging.getLogger())
        self.desc: Optional[str] = desc
        self.counter: int = 0

    def start(self, name: Optional[str] = None, desc: Optional[str] = None):
        if not name:
            self.counter += 1
            name = f"timer_{self.counter}"
        entry = TimerEntry(name, desc)
        entry.start(desc)
        self.timers[name] = entry

    def stop(self, name: str, format_: Optional[str] = None, print_: bool = True):
        """
        Останавливает таймер и форматированно выводит результат/лог.
        format_: None, 's', 'm', 'h', 'HH:MM:SS', 'mm:ss', и т.д.
        """
        entry = self.timers.get(name)
        if not entry:
            raise RuntimeError(f"Timer '{name}' not found")

        diff = entry.stop()  # timedelta
        msg = self._format_time(diff, format_, entry.desc or self.desc)
        if print_:
            print(msg)
        else:
            self.logger.info(msg)
        return diff

    @staticmethod
    def _format_time(diff: timedelta, format_: Optional[str], desc: Optional[str] = None) -> str:
        seconds = diff.total_seconds()
        prefix = f"[{desc}] " if desc else ""
        # Спец. форматы вида HH:MM:SS, mm:ss...
        if format_ and re.search(r'([Hh]{1,2}:)?([Mm]{1,2}:)?[Ss]{1,2}', format_):
            h = int(seconds // 3600)
            m = int((seconds % 3600) // 60)
            s = int(seconds % 60)
            if format_.lower() in ('hh:mm:ss', 'h:m:s'):
                return f"{prefix}{h:02}:{m:02}:{s:02}"
            elif format_.lower() in ('mm:ss', 'm:s'):
                return f"{prefix}{m:02}:{s:02}"
            else:
                return f"{prefix}{seconds:0.5f} sec"
        # 's', 'm', 'h'
        if not format_ or format_ == 's':
            return f"{prefix}{seconds:0.5f} sec"
        elif format_ == 'm':
            minutes = seconds / 60
            if minutes < 0.01:
                return f"{prefix}{seconds:0.5f} sec"
            return f"{prefix}{minutes:0.5f} min"
        elif format_ == 'h':
            hours = seconds / 3600
            minutes = seconds / 60
            if hours < 0.01:
                if minutes < 0.01:
                    return f"{prefix}{seconds:0.5f} sec"
                return f"{prefix}{minutes:0.5f} min"
            return f"{prefix}{hours:0.5f} hr"
        # если что-то непонятное, просто выводим секунды
        return f"{prefix}{seconds:0.5f} sec"