import requests
from typing import Callable, Any

class AshurBotClient:
    """
    Python client for the Ashur Bot API.
    """
    def __init__(self, bot_token: str, bot_secret: str, api_base_url: str = "https://ashurbots.vercel.app"):
        """
        Initialize the AshurBotClient.
        :param bot_token: Bot token string
        :param bot_secret: Bot secret string
        :param api_base_url: Base URL of the Ashur bot API (default: https://ashurbots.vercel.app)
        """
        self.api_base_url = api_base_url.rstrip('/')
        self.bot_token = bot_token
        self.bot_secret = bot_secret 

    def _post(self, endpoint: str, json: dict) -> dict:
        """
        Internal helper to make a POST request to the bot API with auth headers.
        """
        url = f"{self.api_base_url}{endpoint}"
        headers = {
            "Authorization": f"Bot {self.bot_token}",
            "x-bot-secret": self.bot_secret,
            "Content-Type": "application/json"
        }
        response = requests.post(url, json=json, headers=headers)
        response.raise_for_status()
        return response.json() 

    def send_message(self, target: str, message: str) -> dict:
        """
        Send a message as the bot to a user or group chat.
        :param target: User UID or group ID
        :param message: Message text
        :return: API response dict
        """
        return self._post("/api/bot/sendMessage", {"target": target, "message": message})

    def read_messages(self, target: str, limit: int = 20) -> dict:
        """
        Read the latest messages from a user or group chat as the bot.
        :param target: User UID or group ID
        :param limit: Maximum number of messages to return (default 20)
        :return: API response dict
        """
        return self._post("/api/bot/readMessages", {"target": target, "limit": limit}) 

    def edit_message(self, target: str, message_id: str, new_text: str) -> dict:
        """
        Edit a message previously sent by the bot.
        :param target: User UID or group ID
        :param message_id: ID of the message to edit
        :param new_text: New message text
        :return: API response dict
        """
        return self._post("/api/bot/editMessage", {"target": target, "messageId": message_id, "newText": new_text})

    def delete_message(self, target: str, message_id: str) -> dict:
        """
        Delete a message previously sent by the bot.
        :param target: User UID or group ID
        :param message_id: ID of the message to delete
        :return: API response dict
        """
        return self._post("/api/bot/deleteMessage", {"target": target, "messageId": message_id})

    def reply_to(self, target: str, message: str, reply_to_id: str) -> dict:
        """
        Send a reply to a specific message.
        :param target: User UID or group ID
        :param message: Message text
        :param reply_to_id: ID of the message to reply to
        :return: API response dict
        """
        return self._post("/api/bot/replyTo", {"target": target, "message": message, "replyToId": reply_to_id})

    def get_bot_info(self) -> dict:
        """
        Get information about the bot.
        :return: API response dict
        """
        return self._post("/api/bot/getBotInfo", {})

    def update_bot_profile(self, name: str = None, pic: str = None, bio: str = None, webhook_url: str = None) -> dict:
        """
        Update the bot's profile information.
        :param name: New display name
        :param pic: New profile picture URL
        :param bio: New bio
        :param webhook_url: New webhook URL
        :return: API response dict
        """
        data = {}
        if name is not None:
            data["name"] = name
        if pic is not None:
            data["pic"] = pic
        if bio is not None:
            data["bio"] = bio
        if webhook_url is not None:
            data["webhookUrl"] = webhook_url
        return self._post("/api/bot/updateBotProfile", data) 

    def list_chats(self) -> dict:
        """
        List all private chats and groups the bot is a member of.
        :return: API response dict
        """
        return self._post("/api/bot/listChats", {})

    def get_chat_info(self, chat_id: str, is_group: bool = False) -> dict:
        """
        Get information about a chat or group.
        :param chat_id: Chat or group ID
        :param is_group: True if group, False if private chat
        :return: API response dict
        """
        return self._post("/api/bot/getChatInfo", {"chatId": chat_id, "isGroup": is_group})

    def list_group_members(self, group_id: str) -> dict:
        """
        List all members of a group.
        :param group_id: Group ID
        :return: API response dict
        """
        return self._post("/api/bot/listGroupMembers", {"groupId": group_id})

    def add_group_member(self, group_id: str, user_id: str) -> dict:
        """
        Add a user to a group (bot must be admin).
        :param group_id: Group ID
        :param user_id: User ID to add
        :return: API response dict
        """
        return self._post("/api/bot/addGroupMember", {"groupId": group_id, "userId": user_id})

    def remove_group_member(self, group_id: str, user_id: str) -> dict:
        """
        Remove a user from a group (bot must be admin).
        :param group_id: Group ID
        :param user_id: User ID to remove
        :return: API response dict
        """
        return self._post("/api/bot/removeGroupMember", {"groupId": group_id, "userId": user_id})

    def pin_message(self, target: str, message_id: str, is_group: bool = False) -> dict:
        """
        Pin a message in a chat or group.
        :param target: Chat or group ID
        :param message_id: Message ID to pin
        :param is_group: True if group, False if private chat
        :return: API response dict
        """
        return self._post("/api/bot/pinMessage", {"target": target, "messageId": message_id, "isGroup": is_group})

    def unpin_message(self, target: str, message_id: str, is_group: bool = False) -> dict:
        """
        Unpin a message in a chat or group.
        :param target: Chat or group ID
        :param message_id: Message ID to unpin
        :param is_group: True if group, False if private chat
        :return: API response dict
        """
        return self._post("/api/bot/unpinMessage", {"target": target, "messageId": message_id, "isGroup": is_group})

    def set_chat_title(self, group_id: str, name: str) -> dict:
        """
        Set the title of a group (bot must be admin).
        :param group_id: Group ID
        :param name: New group name
        :return: API response dict
        """
        return self._post("/api/bot/setChatTitle", {"groupId": group_id, "name": name})

    def set_group_photo(self, group_id: str, pic: str) -> dict:
        """
        Set the photo of a group (bot must be admin).
        :param group_id: Group ID
        :param pic: New group photo URL
        :return: API response dict
        """
        return self._post("/api/bot/setGroupPhoto", {"groupId": group_id, "pic": pic})

    def leave_chat(self, target: str, is_group: bool = False) -> dict:
        """
        Leave a chat or group.
        :param target: Chat or group ID
        :param is_group: True if group, False if private chat
        :return: API response dict
        """
        return self._post("/api/bot/leaveChat", {"target": target, "isGroup": is_group})

    def get_message_by_id(self, target: str, message_id: str, is_group: bool = False) -> dict:
        """
        Get a message by its ID from a chat or group.
        :param target: Chat or group ID
        :param message_id: Message ID
        :param is_group: True if group, False if private chat
        :return: API response dict
        """
        return self._post("/api/bot/getMessageById", {"target": target, "messageId": message_id, "isGroup": is_group})

    def forward_message(self, from_target: str, message_id: str, to_target: str, from_is_group: bool = False, to_is_group: bool = False) -> dict:
        """
        Forward a message from one chat/group to another.
        :param from_target: Source chat or group ID
        :param message_id: Message ID to forward
        :param to_target: Destination chat or group ID
        :param from_is_group: True if source is group
        :param to_is_group: True if destination is group
        :return: API response dict
        """
        return self._post("/api/bot/forwardMessage", {"fromTarget": from_target, "messageId": message_id, "toTarget": to_target, "fromIsGroup": from_is_group, "toIsGroup": to_is_group})

    def get_bot_stats(self) -> dict:
        """
        Get statistics about the bot (messages sent, groups joined).
        :return: API response dict
        """
        return self._post("/api/bot/getBotStats", {}) 

    def on_webhook(self, handler: Callable[[dict], Any], host: str = "0.0.0.0", port: int = 8080, path: str = "/webhook"):
        """
        Start a simple Flask server to receive webhook events from Ashur bot API.
        :param handler: Function to handle incoming webhook JSON payloads. Receives a dict.
        :param host: Host to bind the server (default: 0.0.0.0)
        :param port: Port to bind the server (default: 8080)
        :param path: Webhook endpoint path (default: /webhook)
        Usage:
            def my_handler(event):
                print(event)
            client.on_webhook(my_handler)
        """
        from flask import Flask, request, jsonify
        app = Flask(__name__)

        @app.route(path, methods=["POST"])
        def webhook():
            data = request.get_json(force=True)
            handler(data)
            return jsonify({"ok": True})

        app.run(host=host, port=port) 