# /mnt/e/genesis-system/core/integrations/slack_bot.py

import os
import logging
import asyncio
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError
from typing import Optional, Dict, Any

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

class SlackBot:
    """
    A class for managing bi-directional communication with Slack.
    """

    def __init__(self, slack_token: str, channel_id: str):
        """
        Initializes the SlackBot with a Slack token and channel ID.

        Args:
            slack_token: The Slack API token.
            channel_id: The ID of the Slack channel to communicate with.
        """
        self.slack_token = slack_token
        self.channel_id = channel_id
        self.client = WebClient(token=self.slack_token)
        self.bot_id: Optional[str] = None
        self._get_bot_id()  # Eagerly get the bot_id upon initialization

    def _get_bot_id(self) -> None:
        """
        Retrieves the bot's user ID from Slack and stores it in `self.bot_id`.
        Handles potential errors from the Slack API.
        """
        try:
            response = self.client.auth_test()
            self.bot_id = response["user_id"]
            logging.info(f"Successfully retrieved bot ID: {self.bot_id}")
        except SlackApiError as e:
            logging.error(f"Error retrieving bot ID: {e}")
            self.bot_id = None  # Ensure bot_id is None if retrieval fails


    async def send_message(self, message: str) -> Optional[str]:
        """
        Sends a message to the specified Slack channel.

        Args:
            message: The message to send.

        Returns:
            The timestamp of the message if successful, otherwise None.
        """
        try:
            result = self.client.chat_postMessage(channel=self.channel_id, text=message)
            return result["ts"]
        except SlackApiError as e:
            logging.error(f"Error sending message: {e}")
            return None

    async def get_channel_history(self, count: int = 10) -> Optional[list[Dict[str, Any]]]:
        """
        Retrieves the recent history of the specified Slack channel.

        Args:
            count: The number of messages to retrieve.

        Returns:
            A list of message dictionaries, or None if an error occurs.
        """
        try:
            result = self.client.conversations_history(channel=self.channel_id, limit=count)
            return result["messages"]
        except SlackApiError as e:
            logging.error(f"Error retrieving channel history: {e}")
            return None

    async def react_to_message(self, timestamp: str, reaction: str) -> bool:
        """
        Adds a reaction to a specific message in the Slack channel.

        Args:
            timestamp: The timestamp of the message.
            reaction: The name of the reaction (e.g., "thumbsup").

        Returns:
            True if the reaction was added successfully, False otherwise.
        """
        try:
            self.client.reactions_add(channel=self.channel_id, timestamp=timestamp, name=reaction)
            return True
        except SlackApiError as e:
            logging.error(f"Error adding reaction: {e}")
            return False


    async def start_listening(self, callback: callable) -> None:
        """
        Starts listening for messages in the Slack channel and calls the callback function
        when a new message is received.  This is a simplified example and would typically
        integrate with a more robust event handling mechanism (like Slack's Events API).

        Args:
            callback: A function that takes a message dictionary as input.  It should handle
                      the processing of the message.

        NOTE:  This method uses a simple polling mechanism, which is not ideal for production.
               A production implementation should use Slack's Events API for real-time updates.
               This is only included for a demonstration of a bi-directional pattern.
        """
        last_processed_ts = None

        while True:
            try:
                messages = await self.get_channel_history(count=5)  # Check for new messages

                if messages:
                    # Filter out messages already processed
                    new_messages = [
                        msg for msg in messages
                        if last_processed_ts is None or float(msg['ts']) > float(last_processed_ts)
                    ]

                    for message in reversed(new_messages):  # Process in chronological order
                        if 'user' != self.bot_id and 'subtype' not in message: # Skip messages from the bot itself and subtypes
                            await callback(message)  # Call the callback function

                    # Update the timestamp of the last processed message
                    if messages:  # Only update if there are messages
                         last_processed_ts = messages[0]['ts']


                await asyncio.sleep(5)  # Poll every 5 seconds
            except Exception as e:
                logging.error(f"Error during listening loop: {e}")
                await asyncio.sleep(60)  # Back off and retry after 60 seconds


# Example Usage (for testing purposes - remove or comment out in production):
async def example_callback(message: Dict[str, Any]) -> None:
    """
    Example callback function to handle incoming Slack messages.
    """
    logging.info(f"Received message: {message['text']} from user {message['user']}")

async def main():
    """
    Main function to demonstrate the SlackBot's functionality.
    """
    slack_token = os.environ.get("SLACK_BOT_TOKEN")  # Load from environment variable
    channel_id = os.environ.get("SLACK_CHANNEL_ID")  # Load from environment variable

    if not slack_token or not channel_id:
        logging.error("Slack bot token and channel ID must be set as environment variables.")
        return

    slack_bot = SlackBot(slack_token, channel_id)

    # Send a message
    timestamp = await slack_bot.send_message("Hello from Genesis! This is a test message.")
    if timestamp:
        logging.info(f"Message sent successfully with timestamp: {timestamp}")

        # React to the message
        await slack_bot.react_to_message(timestamp, "robot_face")

        # Start listening for messages (this will block)
        print("Starting to listen to channel, Ctrl+C to exit...")
        await slack_bot.start_listening(example_callback)
    else:
        logging.error("Failed to send initial test message.")

if __name__ == "__main__":
    asyncio.run(main())