# services/response_manager.py
import json
import base64
from typing import Optional, Dict

# Import the Redis client that all workers will share
from .redis_client import redis_client

class ResponseManager:
    """
    Manages response state in a central Redis store using a FIFO Queue (List)
    for each student_id. This is the definitive, race-condition-safe solution
    for a stateless client.
    """

    def __init__(self):
        """Initializes by connecting to the shared Redis client."""
        if redis_client is None:
            raise ConnectionError("ResponseManager requires a valid Redis connection.")
        self.redis = redis_client
        self.ttl_seconds = 600  # A key will expire 10 mins after the LAST item is added

    def _get_key(self, student_id: str) -> str:
        """Creates a consistent key for the student's queue."""
        return f"student_queue:{student_id}"

    def store_response(self, student_id: str, text: str, audio_filepath: Optional[str] = None, audio_bytes: Optional[bytes] = None) -> None:
        """
        Adds a new response to the END of the queue for a specific student.
        """
        key = self._get_key(student_id)
        
        encoded_audio = base64.b64encode(audio_bytes).decode('utf-8') if audio_bytes else None
        
        payload = {
            "text": text,
            "audio_filepath": audio_filepath,
            "audio_bytes_b64": encoded_audio
        }
        
        # RPUSH adds the new item to the right (end) of the list.
        self.redis.rpush(key, json.dumps(payload))
        # Reset the expiration time for the whole queue each time a new item is added.
        self.redis.expire(key, self.ttl_seconds)

    def get_response(self, student_id: str) -> Dict:
        """
        Atomically retrieves and removes the OLDEST response from the front of the queue.
        """
        key = self._get_key(student_id)
        
        # LPOP atomically retrieves and removes the item from the left (start) of the list.
        json_value = self.redis.lpop(key)

        if not json_value:
            return {"text": None, "audio_filepath": None, "audio_bytes": None}

        # Decode the payload.
        payload = json.loads(json_value)
        
        if payload.get("audio_bytes_b64"):
            payload["audio_bytes"] = base64.b64decode(payload["audio_bytes_b64"])
        else:
            payload["audio_bytes"] = None
        
        del payload["audio_bytes_b64"]
        
        return payload

    def clear_response(self, student_id: str) -> None:
        """
        Completely deletes the entire queue for a student. Useful for '/clear' endpoints.
        """
        key = self._get_key(student_id)
        self.redis.delete(key)

    def is_response_fresh(self, student_id: str) -> bool:
        """
        Checks if there are any items in the student's queue.
        """
        key = self._get_key(student_id)
        # LLEN gets the length of the list. If it's > 0, there's a response ready.
        return self.redis.llen(key) > 0