<?php

declare(strict_types=1);

namespace KimaAI\Ai\Providers\OpenAI\Stream;

use KimaAI\Ai\Providers\DTO\StreamChunk;
use KimaAI\Ai\Providers\Support\SseParser;

final class ResponsesStreamParser {
  /**
   * Parser for SSE frames.
   *
   * @var SseParser
   */
  private SseParser $parser;

  public function __construct(?SseParser $parser = null) {
    $this->parser = $parser ?? new SseParser();
  }

  /**
   * Parse streaming Responses API chunks.
   *
   * @return list<StreamChunk>
   */
  public function push(string $chunk): array {
    $chunks = [];

    foreach ($this->parser->push($chunk) as $frame) {
      $data = trim($frame['data']);
      if ($data === '' || $data === '[DONE]') {
        continue;
      }

      $json = json_decode($data, true);
      if (!is_array($json)) {
        $chunks[] = new StreamChunk(StreamChunk::TYPE_RAW, null, ['data' => $data], []);
        continue;
      }

      $type = (string) ($json['type'] ?? '');

      if ($type === 'response.output_text.delta') {
        $chunks[] = new StreamChunk(
          StreamChunk::TYPE_TEXT_DELTA,
          (string) ($json['delta'] ?? ''),
          [],
          $json
        );
        continue;
      }

      if ($type === 'response.completed') {
        $chunks[] = new StreamChunk(
          StreamChunk::TYPE_COMPLETED,
          null,
          ['response' => $json['response'] ?? null],
          $json
        );
        continue;
      }

      if ($type === 'response.failed' || $type === 'response.incomplete') {
        $chunks[] = new StreamChunk(
          StreamChunk::TYPE_ERROR,
          null,
          ['error' => $json['response']['error'] ?? $json['error'] ?? $json],
          $json
        );
        continue;
      }

      if (preg_match('/^response\.(.+_call)\.(in_progress|searching|completed|failed)$/', $type, $matches)) {
        $chunks[] = new StreamChunk(
          StreamChunk::TYPE_TOOL,
          null,
          [
            'name' => $matches[1],
            'phase' => $matches[2],
          ],
          $json
        );
        continue;
      }

      if ($type === 'response.output_item.added') {
        $item = $json['item'] ?? null;
        if (is_array($item) && ($item['type'] ?? '') === 'function_call') {
          $chunks[] = new StreamChunk(
            StreamChunk::TYPE_TOOL,
            null,
            [
              'name' => (string) ($item['name'] ?? 'tool'),
              'phase' => 'added',
              'call_id' => (string) (($item['call_id'] ?? '') ?: ($item['id'] ?? '')),
              'item_id' => (string) ($item['id'] ?? ''),
            ],
            $json
          );
          continue;
        }
      }

      if ($type === 'response.function_call_arguments.delta' || $type === 'response.function_call_arguments.done') {
        $chunks[] = new StreamChunk(
          StreamChunk::TYPE_TOOL,
          null,
          [
            'phase' => str_ends_with($type, '.done') ? 'arguments_done' : 'arguments_delta',
            'item_id' => (string) ($json['item_id'] ?? ''),
            'delta' => (string) ($json['delta'] ?? ''),
            'arguments' => (string) ($json['arguments'] ?? ''),
          ],
          $json
        );
        continue;
      }

      $chunks[] = new StreamChunk(StreamChunk::TYPE_RAW, null, ['type' => $type], $json);
    }

    return $chunks;
  }
}
