import asyncio
import logging
from abc import ABC, abstractmethod
from collections import deque
from datetime import datetime, timezone

logger = logging.getLogger(__name__)


class BaseService(ABC):
    def __init__(self, name: str, interval_seconds: int = 3600):
        self.name = name
        self.interval_seconds = interval_seconds
        self.log_buffer = deque(maxlen=20)
        self._task = None
        self._running = False
        self._last_run = None
        self._next_run = None
        self._started_at = None

    @property
    def status(self) -> str:
        return "running" if self._running else "stopped"

    @property
    def last_run(self) -> str | None:
        return self._last_run

    @property
    def next_run(self) -> str | None:
        return self._next_run

    @property
    def uptime(self) -> str | None:
        if self._started_at is None:
            return None
        delta = datetime.now(timezone.utc) - self._started_at
        return str(delta).split(".")[0]

    def log(self, message: str) -> None:
        stamp = datetime.now(timezone.utc).strftime("%H:%M:%S")
        entry = f"[{stamp}] {message}"
        self.log_buffer.append(entry)
        logger.info(f"[{self.name}] {message}")

    @abstractmethod
    async def run_once(self) -> None:
        pass

    async def _run_loop(self) -> None:
        self._running = True
        self._started_at = datetime.now(timezone.utc)
        self.log(f"Service started (interval={self.interval_seconds}s)")
        while self._running:
            try:
                self._last_run = datetime.now(timezone.utc).isoformat()
                self._next_run = None
                await self.run_once()
            except asyncio.CancelledError:
                self.log("Service cancelled")
                break
            except Exception as e:
                self.log(f"Error in run_once: {e}")
            if not self._running:
                break
            self._next_run = datetime.now(timezone.utc).isoformat()
            self.log(f"Sleeping for {self.interval_seconds}s")
            try:
                await asyncio.sleep(self.interval_seconds)
            except asyncio.CancelledError:
                self.log("Service cancelled during sleep")
                break
        self._running = False
        self.log("Service stopped")

    async def start(self) -> None:
        if self._running:
            self.log("Already running")
            return
        self._running = True
        self._task = asyncio.create_task(self._run_loop())

    async def stop(self) -> None:
        if not self._running:
            return
        self._running = False
        if self._task is not None:
            self._task.cancel()
            try:
                await asyncio.wait_for(self._task, timeout=10.0)
            except (asyncio.CancelledError, asyncio.TimeoutError):
                pass
            self._task = None
