import requests
import json
import time

now = time.time()

class Update:
    def __init__(self, message_data, chat_id, client):
        self.text = message_data.get("text", "")
        self.message_id = message_data.get("message_id", None)
        self.sender_id = message_data.get("sender_id", None)
        self.chat_id = chat_id
        self.raw = message_data
        self._client = client

    def reply(self, text: str):
        url = f"https://botapi.rubika.ir/v3/{self._client.token}/sendMessage"
        data = {
            "chat_id": self.chat_id,
            "text": text,
            "reply_to_message_id": self.message_id
        }

        resp = requests.post(url, json=data)
        jsn = resp.json()
        if resp.status_code == 200:
            if jsn.get("status") != "OK":
                return resp.text
            return jsn
        return resp.text

class Client:
	
	def __init__(self, token: str):
	    self.token = token
	    jsn = self._request("getMe", {})
	    if isinstance(jsn, dict) and jsn.get("status") != "INVALID_ACCESS":
	        self.username = jsn["data"]["bot"]["username"]
	        print(f"You have successfully logged in to the @{self.username} bot.\n")
	        print("This library was created by the channel @roboka_library")
	    else:
	        raise ValueError(f"❌ Invalid token or connection issue: {jsn}")

	def _request(self, method:str, data: dict, headers: dict = None):
	    url = f"https://botapi.rubika.ir/v3/{self.token}/{method}"
	    response = requests.post(url, json=data, headers=headers)
	    try:
	        jsn = response.json()
	        if response.status_code == 200 and jsn.get("status") == "OK":
	            return jsn
	        return response.text
	    except ValueError:
	        return response.text
			
	def send_text(self, chat_id: str, text: str, message_id: str = None):
	    data = {
	        "chat_id": chat_id,
	        "text": text
	    }
	    if message_id is not None:
	        data["reply_to_message_id"] = message_id
	
	    return self._request("sendMessage", data)
			
	def get_me(self):
	    jsn = self._request("getMe", {})
	    if isinstance(jsn, dict) and jsn.get("status") != "INVALID_ACCESS":
	        return jsn
	    return jsn
	
	def on_message(self, limit=100, poll_interval=0.1, filter: str = None):
	    offset_id = None
	    last_message_id = None

	    while True:
	        try:
	            data = {"limit": limit}
	            if offset_id:
	                data["offset_id"] = offset_id

	            result = self._request("getUpdates", data)

	            if not isinstance(result, dict) or "data" not in result:
	                time.sleep(poll_interval)
	                continue

	            updates = result["data"].get("updates", [])
	            offset_id = result["data"].get("next_offset_id", offset_id)

	            if not updates:
	                time.sleep(poll_interval)
	                continue

	            for update in updates:
	                new_msg = update.get("new_message", {})
	                chat_id = update.get("chat_id")
	                message_id = new_msg.get("message_id")
	                tim = int(new_msg.get("time", 0))

	                if filter is not None:
	                    if filter == "private" and not chat_id.startswith("b"):
	                        continue
	                    elif filter == "group" and not chat_id.startswith("g"):
	                        continue
	                    elif filter == "channel" and not chat_id.startswith("c"):
	                        continue

	                if tim >= now and message_id != last_message_id:
	                    last_message_id = message_id
	                    yield Update(new_msg, chat_id, client=self)

	        except Exception as e:
	            print("❗ خطا در دریافت آپدیت:", e)
	            time.sleep(1)
	            continue
	        
	def create_keypad(self, chat_id: str, text: str, rows: list, message_id: str = None):
	    data = {
	        "chat_id": chat_id,
	        "text": text,
	        "chat_keypad_type": "New",
	        "chat_keypad": {
	            "rows": rows,
	            "resize_keyboard": True,
	            "on_time_keyboard": False
	        }
	    }
	    if message_id is not None:
	        data["reply_to_message_id"] = message_id
	
	    return self._request("sendMessage", data)

	def send_poll(self, chat_id:str, question:str, options:list):
		data = {
		    "chat_id": chat_id,
		    "question": question,
		    "options": options
		}

		return self._request("sendPoll", data)

	def send_location(self, chat_id: str, latitude: float, longitude: float, message_id: str = None):
	    data = {
	        "chat_id": chat_id,
	        "latitude": latitude,
	        "longitude": longitude
	    }
	
	    if message_id is not None:
	        data["reply_to_message_id"] = message_id
	
	    return self._request("sendLocation", data)

	def edit_text(self, chat_id: str, text: str, message_id: int):
	    data = {
	        "chat_id": chat_id,
	        "message_id": message_id,
	        "text": text
	    }
	    return self._request("editMessageText", data)

	def send_contact(self, chat_id: str, first_name: str, last_name: str, phone_number: str, message_id: int = None):
	    data = {
	        "chat_id": chat_id,
	        "first_name": first_name,
	        "last_name": last_name,
	        "phone_number": phone_number
	    }
	
	    if message_id is not None:
	        data["reply_to_message_id"] = message_id
	
	    return self._request("sendContact", data)

	def get_chat(self, chat_id: str):
	    data = {
	        "chat_id": chat_id
	    }
	    return self._request("getChat", data)

	def forward_message(self, from_chat_id: str, message_id: int, to_chat_id: str):
	    data = {
	        "from_chat_id": from_chat_id,
	        "message_id": message_id,
	        "to_chat_id": to_chat_id
	    }
	    return self._request("forwardMessage", data)

	def delete_message(self, chat_id: str, message_id: int):
	    data = {
	        "chat_id": chat_id,
	        "message_id": message_id
	    }
	    return self._request("deleteMessage", data)

	def create_inline_keypad(self, chat_id: str, text: str, rows: list, message_id: str = None):
	    data = {
	        "chat_id": chat_id,
	        "text": text,
	        "inline_keypad": {
	            "rows": rows
	        }
	    }
	
	    if message_id is not None:
	        data["reply_to_message_id"] = message_id
	
	    return self._request("sendMessage", data)

	def get_upload_url(self, type: str):
	    data = {
	        "type": type
	    }
	    response = self._request("requestSendFile", data)
	    return response["data"]["upload_url"]

	def get_file_id(self, url: str, file_name: str, file_path: str) -> str:
	    with open(file_path, "rb") as f:
	        files = {
	            "file": (file_name, f, "application/octet-stream")
	        }
	        response = requests.post(url, files=files, verify=False)
	        response.raise_for_status()
	
	    return response.json()["data"]["file_id"]
	
	def send_file(self, chat_id: str, text: str, file_name: str, file_path: str, file_type: str, message_id: str = None):
	    upload_url = self.get_upload_url(file_type)
	    file_id = self.get_file_id(upload_url, file_name, file_path)
	
	    data = {
	        "chat_id": chat_id,
	        "file_id": file_id,
	        "text": text
	    }
	
	    if message_id is not None:
	        data["reply_to_message_id"] = message_id
	
	    return self._request("sendFile", data)

	def send_image(self, chat_id: str, text: str, file_name: str, file_path: str, message_id: str = None):
	    upload_url = self.get_upload_url("Image")
	    file_id = self.get_file_id(upload_url, file_name, file_path)
	
	    data = {
	        "chat_id": chat_id,
	        "file_id": file_id,
	        "text": text
	    }
	
	    if message_id is not None:
	        data["reply_to_message_id"] = message_id
	
	    return self._request("sendFile", data)

	def send_video(self, chat_id: str, text: str, file_name: str, file_path: str, message_id: str = None):
	    upload_url = self.get_upload_url("Video")
	    file_id = self.get_file_id(upload_url, file_name, file_path)
	
	    data = {
	        "chat_id": chat_id,
	        "file_id": file_id,
	        "text": text
	    }
	
	    if message_id is not None:
	        data["reply_to_message_id"] = message_id
	
	    return self._request("sendFile", data)

	def get_updates(self, limit: str, offset_id: str = None):
	    data = {
	        "limit": limit
	    }
	
	    if offset_id is not None:
	        data["offset_id"] = offset_id
	
	    return self._request("getUpdates", data)

	def edit_keypad(self, chat_id: str, rows: list):
	    data = {
	        "chat_id": chat_id,
	        "chat_keypad_type": "New",
	        "chat_keypad": {
	            "rows": rows,
	            "resize_keyboard": True,
	            "on_time_keyboard": False
	        }
	    }
	
	    return self._request("editChatKeypad", data)

	def set_commands(self, commands: list):
	    data = {
	        "bot_commands": commands
	    }
	
	    return self._request("setCommands", data)

	def set_webhook(self, url: str, type: str):
	    data = {
	        "url": url,
	        "type": type
	    }
	
	    return self._request("updateBotEndpoints", data)