import json
import threading
import time
import websocket
import logging
from typing import Optional

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

class RWLEventEmitter:
    """
    Emits WebSocket events for RWL Runner status.
    """

    def __init__(self, ws_url: str, story_id: str, title: str):
        """
        Initializes the RWLEventEmitter.

        Args:
            ws_url: The WebSocket URL to connect to.
            story_id: The ID of the story being run.
            title: The title of the story.
        """
        self.ws_url = ws_url
        self.story_id = story_id
        self.title = title
        self.ws = None  # type: Optional[websocket.WebSocket]
        self._connect()

    def _connect(self):
        """
        Attempts to connect to the WebSocket server.  Retries every 5 seconds if connection fails.
        """
        try:
            self.ws = websocket.create_connection(self.ws_url)
            logging.info(f"Connected to WebSocket server at {self.ws_url}")
        except Exception as e:
            logging.error(f"Failed to connect to WebSocket server at {self.ws_url}: {e}")
            self.ws = None
            threading.Timer(5, self._connect).start()  # Retry connection every 5 seconds


    def _send_event(self, event_type: str, attempt: int = 0, max_attempts: int = 0, status: str = ""):
        """
        Sends an event to the WebSocket server in a non-blocking manner.

        Args:
            event_type: The type of event (e.g., "story_start", "story_progress", "story_complete", "story_failed").
            attempt: The current attempt number (if applicable).
            max_attempts: The maximum number of attempts (if applicable).
            status: The status of the story (if applicable).
        """
        timestamp = time.time()
        event = {
            "type": event_type,
            "story_id": self.story_id,
            "title": self.title,
            "attempt": attempt,
            "max_attempts": max_attempts,
            "status": status,
            "timestamp": timestamp
        }

        def send_in_thread(event_data: str):
            """
            Sends the event data in a separate thread.

            Args:
                event_data: The JSON string representation of the event.
            """
            try:
                if self.ws:
                    self.ws.send(event_data)
                    logging.info(f"Sent event: {event_data}")
                else:
                    logging.warning("WebSocket connection not available, event not sent.")
            except Exception as e:
                logging.error(f"Failed to send event: {e}")

        event_json = json.dumps(event)
        thread = threading.Thread(target=send_in_thread, args=(event_json,))
        thread.daemon = True  # Allow the program to exit even if this thread is running
        thread.start()

    def emit_story_start(self):
        """
        Emits a "story_start" event.
        """
        self._send_event(event_type="story_start")

    def emit_story_progress(self, attempt: int, max_attempts: int):
        """
        Emits a "story_progress" event.

        Args:
            attempt: The current attempt number.
            max_attempts: The maximum number of attempts.
        """
        self._send_event(event_type="story_progress", attempt=attempt, max_attempts=max_attempts)

    def emit_story_complete(self):
        """
        Emits a "story_complete" event.
        """
        self._send_event(event_type="story_complete", status="success")

    def emit_story_failed(self):
        """
        Emits a "story_failed" event.
        """
        self._send_event(event_type="story_failed", status="failure")

    def close(self):
        """
        Closes the WebSocket connection.
        """
        if self.ws:
            self.ws.close()
            logging.info("WebSocket connection closed.")