from django.http import StreamingHttpResponse

class SSEManager:
    _listeners = {}

    @classmethod
    def register_listener(cls, model_name, object_id, field_name):
        if model_name not in cls._listeners:
            cls._listeners[model_name] = {}
        if object_id not in cls._listeners[model_name]:
            cls._listeners[model_name][object_id] = {}
        if field_name not in cls._listeners[model_name][object_id]:
            cls._listeners[model_name][object_id][field_name] = []
        return cls._listeners[model_name][object_id][field_name]

    @classmethod
    def notify_listeners(cls, model_name, object_id, field_name, new_value):
        listeners = cls._listeners.get(model_name, {}).get(object_id, {}).get(field_name, [])
        print(f"Notifying {len(listeners)} listeners for {model_name} {object_id} {field_name} with new value {new_value}")
        for listener in listeners:
            listener(new_value)  # Call each listener callback directly with the new value

    @classmethod
    def stream_updates(cls, request, app_label, model_name, object_id, field_name):
        def event_stream():
            listeners = cls.register_listener(model_name, object_id, field_name)

            from django.apps import apps
            model = apps.get_model(app_label, model_name)
            try:
                instance = model.objects.get(pk=object_id)
                last_value = getattr(instance, field_name)
            except model.DoesNotExist:
                last_value = None

            # Function to immediately send updates via the StreamingHttpResponse
            def send_update(new_value):
                nonlocal last_value
                if new_value != last_value:
                    last_value = new_value
                    print(f"Sending SSE update: {new_value}")
                    yield f"data: {new_value}\n\n"  # Yield the data directly to the client

            # Define a listener that calls the send_update function
            def listener_callback(new_value):
                # Yield the updates back to the StreamingHttpResponse
                for update in send_update(new_value):
                    yield update

            # Append the listener (a function that yields updates)
            print(f"Appending listener for {model_name} {object_id} {field_name}")
            listeners.append(listener_callback)

            # Send the initial value if it exists
            yield from send_update(last_value)

            # Keep the connection open and handle client disconnects
            try:
                while True:
                    if request.META.get('HTTP_CONNECTION', '').lower() == 'close':
                        break
            except GeneratorExit:
                print(f"Removing listener for {model_name} {object_id} {field_name}")
                listeners.remove(listener_callback)
                raise

        # Return the event stream as a StreamingHttpResponse
        return StreamingHttpResponse(event_stream(), content_type='text/event-stream')
