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

logger = logging.getLogger("rubio")

class Message:
    def __init__(self, bot, data: Dict[str, Any]):
        self.bot = bot
        self.raw = data
        self.chat_id = data.get("chat_id")
        
        msg = data.get("new_message", data.get("inline_message", {}))
        self.message_id = msg.get("message_id")
        self.sender_id = msg.get("sender_id")
        self.text = msg.get("text", "")
        self.reply_to_message_id = msg.get("reply_to_message_id")

    async def reply(self, text: str, **kwargs):
        return await self.bot.send_message(self.chat_id, text, reply_to_message_id=self.message_id, **kwargs)

    async def edit(self, text: str):
        return await self.bot.edit_message_text(self.chat_id, self.message_id, text)

    async def delete(self):
        return await self.bot.delete_message(self.chat_id, self.message_id)

class Bot:
    def __init__(self, token: str):
        self.token = token
        self.base_url = f"https://botapi.rubika.ir/v3/{token}/"
        self.client = httpx.AsyncClient(base_url=self.base_url, timeout=40.0)
        self.handlers = []
        self.is_running = False

    def on_message(self):
        def decorator(func):
            self.handlers.append(func)
            return func
        return decorator

    async def request(self, method: str, data: Optional[Dict] = None):
        try:
            resp = await self.client.post(method, json=data or {})
            res = resp.json()
            if res.get("status") == "OK":
                return res.get("data", res)
            return {"ok": False, "error": res.get("description")}
        except Exception as e:
            return {"ok": False, "error": str(e)}

    async def get_me(self): return await self.request("getMe")
    async def send_message(self, chat_id, text, **kw): return await self.request("sendMessage", {"chat_id": chat_id, "text": text, **kw})
    async def edit_message_text(self, chat_id, message_id, text): return await self.request("editMessageText", {"chat_id": chat_id, "message_id": message_id, "text": text})
    async def delete_message(self, chat_id, message_id): return await self.request("deleteMessage", {"chat_id": chat_id, "message_id": message_id})
    async def get_updates(self, offset_id=None, limit=100): return await self.request("getUpdates", {"offset_id": offset_id, "limit": limit})

    async def run(self):
        print("\x1b[38;5;208m[ Rubio ] \x1b[0mStarting Light Engine (v8.0.2)...")
        self.is_running = True
        
        # Strictly catch the absolute latest offset before starting the loop
        updates = await self.get_updates(limit=1)
        if updates and updates.get("next_offset_id"):
            self.offset = updates.get("next_offset_id")
            # Extra safety: do one more poll to clear any immediate race condition
            await self.get_updates(self.offset)
            print(f"\x1b[32m[+] Rubio Synced. Only new messages after this point will be processed.\x1b[0m")
        else:
            self.offset = None
            print("\x1b[33m[!] Initial sync returned no offset. Listening for all messages.\x1b[0m")

        while self.is_running:
            try:
                res = await self.get_updates(self.offset)
                if res and res.get("updates"):
                    for u in res["updates"]:
                        msg = Message(self, u)
                        for handler in self.handlers:
                            asyncio.create_task(handler(self, msg))
                    self.offset = res.get("next_offset_id")
                await asyncio.sleep(0.1)
            except Exception as e:
                await asyncio.sleep(1)

    async def __aenter__(self): return self
    async def __aexit__(self, *a): await self.client.aclose()
