"""
Zehnex - Ultra-fast & Flexible Telegram Bot Framework.
Combines the simplicity of Telebot with the power of Aiogram.
"""

import asyncio
import logging
import time
from typing import Callable, Dict, List, Optional, Any, Union
import httpx

logger = logging.getLogger("zehnex")


class Zehnex:
    """
    Zehnex Bot Engine.
    """

    BASE_URL = "https://api.telegram.org/bot{token}/{method}"

    def __init__(
        self,
        token: str,
        workers: int = 15,
        timeout: int = 30,
        parse_mode: str = "HTML",
    ):
        self.token = token
        self.workers = workers
        self.timeout = timeout
        self.parse_mode = parse_mode
        self._handlers: Dict[str, List[tuple]] = {}
        self._commands: Dict[str, Callable] = {}
        self._middlewares: List[Callable] = []
        self._offset = 0
        self._running = False
        self._client: Optional[httpx.AsyncClient] = None
        self._semaphore = asyncio.Semaphore(workers)
        self._start_time = time.time()

        logging.basicConfig(
            format="%(asctime)s [ZEHNEX] %(levelname)s: %(message)s",
            level=logging.INFO,
        )

    # ─────────────────────────── Handlers (Aiogram style) ───────────────────────────

    def message(self, *filters, commands: Optional[List[str]] = None):
        """Aiogram style message handler."""
        def decorator(func: Callable):
            if "message" not in self._handlers:
                self._handlers["message"] = []
            
            actual_filters = list(filters)
            if commands:
                from zehnex.filters import Filter
                actual_filters.append(Filter.command(commands))
            
            self._handlers["message"].append((func, actual_filters))
            return func
        return decorator

    def callback_query(self, *filters):
        """Aiogram style callback handler."""
        def decorator(func: Callable):
            if "callback_query" not in self._handlers:
                self._handlers["callback_query"] = []
            self._handlers["callback_query"].append((func, list(filters)))
            return func
        return decorator

    # ─────────────────────────── Handlers (Telebot style) ───────────────────────────

    def message_handler(self, commands: Optional[List[str]] = None, func: Optional[Callable] = None):
        """Telebot style message handler."""
        def decorator(handler_func: Callable):
            if "message" not in self._handlers:
                self._handlers["message"] = []
            
            filters = []
            if commands:
                from zehnex.filters import Filter
                filters.append(Filter.command(commands))
            if func:
                filters.append(func)
                
            self._handlers["message"].append((handler_func, filters))
            return handler_func
        return decorator

    # ─────────────────────────── API Methods ──────────────────────────

    async def _request(self, method: str, **kwargs) -> Dict:
        url = self.BASE_URL.format(token=self.token, method=method)
        try:
            if not self._client:
                self._client = httpx.AsyncClient(timeout=self.timeout)
            response = await self._client.post(url, json=kwargs)
            data = response.json()
            if not data.get("ok"):
                logger.error(f"API Error [{method}]: {data.get('description')}")
            return data.get("result", {})
        except Exception as e:
            logger.error(f"Request error [{method}]: {e}")
            return {}

    async def send_message(self, chat_id: Union[int, str], text: str, **kwargs) -> Dict:
        params = {"chat_id": chat_id, "text": text, "parse_mode": self.parse_mode, **kwargs}
        return await self._request("sendMessage", **params)

    async def start_polling(self):
        """Start the bot polling."""
        self._running = True
        self._client = httpx.AsyncClient(limits=httpx.Limits(max_connections=100))
        logger.info("🚀 Zehnex is online and running...")
        
        while self._running:
            try:
                updates = await self._request("getUpdates", offset=self._offset, timeout=20)
                if updates:
                    tasks = [self._process_update(u) for u in updates]
                    await asyncio.gather(*tasks, return_exceptions=True)
            except asyncio.CancelledError:
                break
            except Exception as e:
                logger.error(f"Polling error: {e}")
                await asyncio.sleep(1)

    async def _process_update(self, update: Dict):
        async with self._semaphore:
            from zehnex.context import Context
            update_id = update.get("update_id", 0)
            self._offset = max(self._offset, update_id + 1)
            ctx = Context(update=update, bot=self)
            
            update_type = "message" if "message" in update else ("callback_query" if "callback_query" in update else None)
            if update_type and update_type in self._handlers:
                for handler, filters in self._handlers[update_type]:
                    passed = True
                    for f in filters:
                        if asyncio.iscoroutinefunction(f):
                            if not await f(ctx): passed = False; break
                        elif callable(f):
                            if not f(ctx): passed = False; break
                    if passed:
                        await handler(ctx)
                        break

    def run(self):
        """Sync entry point."""
        try:
            asyncio.run(self.start_polling())
        except KeyboardInterrupt:
            logger.info("🛑 Zehnex stopped.")
