<?php

declare(strict_types=1);

namespace KimaAI\Ai\Providers\Http;

use RuntimeException;

final class CurlHttpClient implements HttpClientInterface {
  public function send(HttpRequest $request): HttpResponse {
    $ch = $this->createHandle($request, true);

    $response = curl_exec($ch);
    if ($response === false) {
      $message = 'cURL error: ' . curl_error($ch);
      curl_close($ch);
      throw new RuntimeException($message);
    }

    $status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    return new HttpResponse($status, (string) $response);
  }

  public function stream(HttpRequest $request): iterable {
    $queue = [];
    $error = null;
    $status = 0;

    $ch = $this->createHandle($request, false);
    curl_setopt($ch, CURLOPT_WRITEFUNCTION, static function ($ch, string $data) use (&$queue) {
      $queue[] = $data;
      return strlen($data);
    });

    $multi = curl_multi_init();
    curl_multi_add_handle($multi, $ch);

    do {
      do {
        $multi_status = curl_multi_exec($multi, $running);
      } while ($multi_status === CURLM_CALL_MULTI_PERFORM);

      while ($queue !== []) {
        yield array_shift($queue);
      }

      if ($running) {
        curl_multi_select($multi, 0.1);
      }
    } while ($running && $multi_status === CURLM_OK);

    while ($queue !== []) {
      yield array_shift($queue);
    }

    if ($multi_status !== CURLM_OK) {
      $error = 'cURL multi error: ' . curl_multi_strerror($multi_status);
    }
    elseif (curl_errno($ch)) {
      $error = 'cURL error: ' . curl_error($ch);
    }
    else {
      $status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
    }

    curl_multi_remove_handle($multi, $ch);
    curl_multi_close($multi);
    curl_close($ch);

    if ($error !== null) {
      throw new RuntimeException($error);
    }

    if ($status >= 400) {
      throw new RuntimeException('API error: HTTP ' . $status . ' - ' . trim(implode('', $queue)));
    }
  }

  private function createHandle(HttpRequest $request, bool $return_transfer) {
    $url = $request->url;
    $method = strtoupper($request->method);

    if ($method === 'GET' && $request->payload !== []) {
      $query = http_build_query($request->payload);
      $url .= (str_contains($url, '?') ? '&' : '?') . $query;
    }

    $ch = curl_init($url);
    curl_setopt_array($ch, [
      CURLOPT_CUSTOMREQUEST => $method,
      CURLOPT_RETURNTRANSFER => $return_transfer,
      CURLOPT_HTTPHEADER => $request->headers,
      CURLOPT_TIMEOUT => $request->timeout,
      CURLOPT_CAINFO => ABSPATH . WPINC . '/certificates/ca-bundle.crt',
    ]);

    if ($method !== 'GET' && $request->payload !== []) {
      curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($request->payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
    }

    return $ch;
  }
}
