<?php

namespace boru\boruai\Openai\Stream\Listener;

use boru\boruai\Openai\Stream\AbstractStreamEventListener;
use boru\boruai\Openai\Stream\StreamEvent;

class OutputBufferListener extends AbstractStreamEventListener
{
    /** @var string */
    private $buffer = '';

    /** @var bool */
    private $done = false;

    /** @var callable[] */
    private $deltaCallbacks = array();

    /** @var callable[] */
    private $doneCallbacks = array();

    /**
     * Register a callback for output_text deltas.
     *
     * Signature: function (StreamEvent $event, $delta, $bufferSoFar): void
     *
     * @param callable $callback
     * @return $this
     * @throws \Exception
     */
    public function onDelta($callback)
    {
        if (!is_callable($callback)) {
            throw new \Exception("onDelta callback must be callable");
        }
        $this->deltaCallbacks[] = $callback;
        return $this;
    }

    /**
     * Register a callback when output_text streaming is done.
     *
     * Signature: function (StreamEvent $event, $finalBuffer): void
     *
     * @param callable $callback
     * @return $this
     * @throws \Exception
     */
    public function onDone($callback)
    {
        if (!is_callable($callback)) {
            throw new \Exception("onDone callback must be callable");
        }
        $this->doneCallbacks[] = $callback;
        return $this;
    }

    public function handle(StreamEvent $event)
    {
        $type = $event->type();

        if ($type === StreamEvent::RESPONSE_OUTPUT_TEXT_DELTA) {
            $delta = $event->delta();
            if (is_string($delta)) {
                $this->buffer .= $delta;

                // Fire delta callbacks
                if (!empty($this->deltaCallbacks)) {
                    foreach ($this->deltaCallbacks as $cb) {
                        call_user_func($cb, $event, $delta, $this->buffer);
                    }
                }
            }
            return true;
        }

        if ($type === StreamEvent::RESPONSE_OUTPUT_TEXT_DONE) {
            $this->done = true;

            // Fire done callbacks
            if (!empty($this->doneCallbacks)) {
                foreach ($this->doneCallbacks as $cb) {
                    call_user_func($cb, $event, $this->buffer);
                }
            }
            return true;
        }

        return false;
    }

    public function getBuffer()
    {
        return $this->buffer;
    }

    public function isDone()
    {
        return $this->done ? true : false;
    }
}
