#!/usr/bin/env php
<?php
// PUBLIC DOMAIN - NO LICENSE, NO WARRANTY
// Copyright 2025 TimeHexOn & foxhop & russell@unturf
// https://www.permacomputer.com

/**
 * UncloseAI - PHP client for OpenAI-compatible APIs with streaming support
 */

class UncloseAI {
    private $models = [];
    private $tts_endpoints = [];
    private $api_key;
    private $timeout;
    private $debug;

    public function __construct($config = []) {
        $this->api_key = $config['api_key'] ?? null;
        $this->timeout = $config['timeout'] ?? 30;
        $this->debug = $config['debug'] ?? false;

        $endpoints = $config['endpoints'] ?? $this->discoverEndpointsFromEnv('MODEL_ENDPOINT');
        $ttsEndpoints = $config['tts_endpoints'] ?? $this->discoverEndpointsFromEnv('TTS_ENDPOINT');

        if ($this->debug) {
            echo "[DEBUG] Initialized with " . count($endpoints) . " endpoint(s)\n";
        }

        $this->discoverModels($endpoints);
        $this->tts_endpoints = $ttsEndpoints;
    }

    public function listModels() {
        return $this->models;
    }

    public function chat($messages, $options = []) {
        $model = $options['model'] ?? null;
        $modelInfo = $this->resolveModel($model);

        $payload = [
            'model' => $modelInfo['id'],
            'messages' => $messages,
            'max_tokens' => $options['max_tokens'] ?? 100,
            'temperature' => $options['temperature'] ?? 0.7
        ];

        $response = $this->httpRequest($modelInfo['endpoint'] . '/chat/completions', 'POST', $payload);
        return json_decode($response, true);
    }

    public function chatStream($messages, $options, $callback) {
        $model = $options['model'] ?? null;
        $modelInfo = $this->resolveModel($model);

        $payload = [
            'model' => $modelInfo['id'],
            'messages' => $messages,
            'max_tokens' => $options['max_tokens'] ?? 500,
            'temperature' => $options['temperature'] ?? 0.7,
            'stream' => true
        ];

        $url = $modelInfo['endpoint'] . '/chat/completions';
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
        curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);

        $buffer = '';
        curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($curl, $data) use ($callback, &$buffer) {
            $buffer .= $data;
            $lines = explode("\n", $buffer);
            $buffer = array_pop($lines);

            foreach ($lines as $line) {
                if (strpos($line, 'data: ') === 0) {
                    $json = substr($line, 6);
                    if (trim($json) === '[DONE]') {
                        return strlen($data);
                    }
                    $chunk = json_decode($json, true);
                    if ($chunk) {
                        call_user_func($callback, $chunk);
                    }
                }
            }
            return strlen($data);
        });

        curl_exec($ch);
        if (curl_errno($ch)) {
            throw new Exception(curl_error($ch));
        }
        curl_close($ch);
    }

    public function tts($text, $voice = 'alloy', $model = 'tts-1') {
        if (empty($this->tts_endpoints)) {
            throw new Exception('No TTS endpoints available');
        }

        $payload = [
            'model' => $model,
            'voice' => $voice,
            'input' => $text
        ];

        return $this->httpRequest($this->tts_endpoints[0] . '/audio/speech', 'POST', $payload);
    }

    private function discoverEndpointsFromEnv($prefix) {
        $endpoints = [];
        for ($i = 1; $i < 10000; $i++) {
            $endpoint = getenv("{$prefix}_{$i}");
            if ($endpoint === false) break;
            $endpoints[] = $endpoint;
        }
        return $endpoints;
    }

    private function discoverModels($endpoints) {
        foreach ($endpoints as $endpoint) {
            if ($this->debug) {
                echo "[DEBUG] Discovering from: $endpoint\n";
            }

            try {
                $response = $this->httpRequest("$endpoint/models", 'GET');
                $data = json_decode($response, true);

                foreach ($data['data'] as $model) {
                    $maxTokens = $model['max_model_len'] ?? 8192;
                    $this->models[] = [
                        'id' => $model['id'],
                        'endpoint' => $endpoint,
                        'max_tokens' => $maxTokens
                    ];

                    if ($this->debug) {
                        echo "[DEBUG] Discovered: {$model['id']}\n";
                    }
                }
            } catch (Exception $e) {
                if ($this->debug) {
                    echo "[DEBUG] Error: {$e->getMessage()}\n";
                }
            }
        }
    }

    private function resolveModel($model) {
        if (empty($this->models)) {
            throw new Exception('No models available');
        }

        if ($model === null) {
            return $this->models[0];
        }

        foreach ($this->models as $m) {
            if ($m['id'] === $model) {
                return $m;
            }
        }

        throw new Exception("Model '$model' not found");
    }

    private function httpRequest($url, $method, $payload = null) {
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);

        if ($method === 'POST' && $payload) {
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
            curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
        }

        if ($this->api_key) {
            curl_setopt($ch, CURLOPT_HTTPHEADER, [
                'Content-Type: application/json',
                "Authorization: Bearer {$this->api_key}"
            ]);
        }

        $response = curl_exec($ch);

        if (curl_errno($ch)) {
            throw new Exception(curl_error($ch));
        }

        curl_close($ch);
        return $response;
    }
}

// Demo when run as script
if (basename(__FILE__) === basename($_SERVER['PHP_SELF'])) {
    echo "=== UncloseAI PHP Client (with Streaming) ===\n\n";

    $client = new UncloseAI(['debug' => true]);

    if (empty($client->listModels())) {
        echo "ERROR: No models discovered. Set environment variables:\n";
        echo "  MODEL_ENDPOINT_1, MODEL_ENDPOINT_2, etc.\n";
        exit(1);
    }

    $models = $client->listModels();
    echo "\nDiscovered " . count($models) . " model(s):\n";
    foreach ($models as $m) {
        echo "  - {$m['id']} (max_tokens: {$m['max_tokens']})\n";
    }
    echo "\n";

    // Non-streaming chat
    echo "=== Non-Streaming Chat ===\n";
    $response = $client->chat([
        ['role' => 'system', 'content' => 'You are a helpful AI assistant.'],
        ['role' => 'user', 'content' => 'Explain quantum computing in one sentence.']
    ]);
    echo "Response: " . $response['choices'][0]['message']['content'] . "\n\n";

    // Streaming chat
    echo "=== Streaming Chat ===\n";
    $modelId = count($models) > 1 ? $models[1]['id'] : null;
    echo "Model: " . ($modelId ?? $models[0]['id']) . "\n";
    echo "Response: ";

    $client->chatStream([
        ['role' => 'system', 'content' => 'You are a coding assistant.'],
        ['role' => 'user', 'content' => 'Write a PHP function to check if a number is prime']
    ], ['model' => $modelId, 'max_tokens' => 200], function($chunk) {
        if (isset($chunk['choices'][0]['delta']['content'])) {
            echo $chunk['choices'][0]['delta']['content'];
        }
    });

    echo "\n\n";

    // TTS
    if (!empty($client->listModels())) {
        echo "=== TTS Speech Generation ===\n";
        try {
            $audioData = $client->tts('Hello from UncloseAI PHP client! This demonstrates streaming support.');
            file_put_contents('speech.mp3', $audioData);
            echo "[OK] Speech file created: speech.mp3 (" . strlen($audioData) . " bytes)\n\n";
        } catch (Exception $e) {
            echo "[ERROR] TTS Error: " . $e->getMessage() . "\n\n";
        }
    }

    echo "=== Examples Complete ===\n";
}
