<?php
declare(strict_types=1);

/**
 * Klient HTTP do komunikacji z Python FastAPI serwisem plików.
 *
 * Serwis działa na 127.0.0.1:8001 i jest dostępny wyłącznie z serwera.
 * PHP komunikuje się z nim przez cURL, przekazując klucz API w nagłówku X-API-Key.
 *
 * Konfiguracja (zmienne środowiskowe lub bezpośrednio w konstruktorze):
 *   FILE_API_URL  – bazowy URL serwisu, domyślnie http://127.0.0.1:8001
 *   FILE_API_KEY  – klucz API (musi być taki sam jak w api/.env)
 *
 * Przykład użycia:
 *   $client = get_file_api_client();
 *   $result = $client->upload('admin_chat', $_FILES['file']['tmp_name'],
 *                             $_FILES['file']['name'], $_FILES['file']['type']);
 *   $filePath = $result['path']; // np. "admin_chat/abc123.jpg"
 */

class FileApiClient
{
    private string $baseUrl;
    private string $apiKey;
    private int $timeoutSeconds;

    public function __construct(
        string $baseUrl = 'http://127.0.0.1:8001',
        string $apiKey = '',
        int $timeoutSeconds = 30
    ) {
        $this->baseUrl = rtrim($baseUrl, '/');
        $this->apiKey = $apiKey !== '' ? $apiKey : (string)(getenv('FILE_API_KEY') ?: '');
        $this->timeoutSeconds = $timeoutSeconds;
    }

    private static function stringifyDetail($value): string
    {
        if (is_string($value)) {
            return trim($value);
        }

        if (is_array($value)) {
            $parts = [];
            foreach ($value as $item) {
                if (is_string($item)) {
                    $parts[] = trim($item);
                    continue;
                }
                if (is_array($item)) {
                    $loc = '';
                    if (!empty($item['loc']) && is_array($item['loc'])) {
                        $loc = implode('.', array_map('strval', $item['loc']));
                    }
                    $msg = isset($item['msg']) ? trim((string)$item['msg']) : '';
                    $type = isset($item['type']) ? trim((string)$item['type']) : '';
                    $piece = '';
                    if ($loc !== '') {
                        $piece .= '[' . $loc . '] ';
                    }
                    if ($msg !== '') {
                        $piece .= $msg;
                    }
                    if ($type !== '') {
                        $piece .= ($piece !== '' ? ' ' : '') . '(' . $type . ')';
                    }
                    if ($piece !== '') {
                        $parts[] = $piece;
                    }
                }
            }

            if (!empty($parts)) {
                return implode(' | ', $parts);
            }

            $json = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            if (is_string($json) && $json !== '') {
                return $json;
            }
        }

        return '';
    }

    /**
     * Prześlij plik do serwisu i zapisz na dysku.
     *
     * @param  string $endpoint   Endpoint routera, np. 'admin_chat' lub 'admin_tasks'
     * @param  string $tmpPath    Ścieżka do tymczasowego pliku z $_FILES[...]['tmp_name']
     * @param  string $fileName   Oryginalna nazwa pliku
     * @param  string $mimeType   Typ MIME
     * @return array{stored_name: string, original_name: string, mime: string, size: int, path: string}
     * @throws RuntimeException   Jeśli upload się nie powiódł
     */
    public function upload(
        string $endpoint,
        string $tmpPath,
        string $fileName,
        string $mimeType
    ): array {
        if (!is_uploaded_file($tmpPath)) {
            throw new RuntimeException('Nieprawidłowy plik do przesłania (nie pochodzi z $_FILES)');
        }

        $url = $this->baseUrl . '/' . ltrim($endpoint, '/') . '/upload';

        $ch = curl_init($url);
        if ($ch === false) {
            throw new RuntimeException('Nie można zainicjować cURL');
        }

        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST           => true,
            CURLOPT_TIMEOUT        => $this->timeoutSeconds,
            CURLOPT_HTTPHEADER     => ['X-API-Key: ' . $this->apiKey],
            CURLOPT_POSTFIELDS     => [
                'file' => new CURLFile($tmpPath, $mimeType, $fileName),
            ],
            // Wyłącz weryfikację SSL (localhost nie ma certyfikatu)
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => 0,
        ]);

        $response = curl_exec($ch);
        $httpCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $curlErr  = curl_error($ch);
        curl_close($ch);

        if ($curlErr !== '') {
            throw new RuntimeException('Błąd połączenia z serwisem plików: ' . $curlErr);
        }

        $data = json_decode((string)$response, true);

        if ($httpCode !== 200 || !is_array($data) || empty($data['success'])) {
            $detail = '';
            if (is_array($data)) {
                $detail = self::stringifyDetail($data['detail'] ?? '');
                if ($detail === '') {
                    $detail = self::stringifyDetail($data['error'] ?? '');
                }
            }
            if ($detail === '') {
                $raw = trim((string)$response);
                if ($raw !== '') {
                    $detail = $raw;
                }
            }
            if ($detail === '') {
                $detail = 'nieznany błąd';
            }

            $exceptionCode = ($httpCode >= 400 && $httpCode <= 599) ? $httpCode : 0;
            throw new RuntimeException('Serwis plików zwrócił błąd: ' . $detail . ' (HTTP ' . $httpCode . ')', $exceptionCode);
        }

        return (array)($data['data'] ?? []);
    }

    /**
     * Pobierz plik z serwisu jako strumień binarny i przekaż go do klienta.
     * Metoda ustawia nagłówki HTTP i wypisuje ciało pliku – użyj jej tuż przed exit().
     *
     * @param  string $endpoint    Endpoint routera, np. 'admin_chat'
     * @param  string $storedName  Nazwa pliku na dysku, np. 'abc123.jpg'
     * @param  bool   $inline      true = wyświetl w przeglądarce, false = pobierz
     * @throws RuntimeException    Jeśli plik nie istnieje lub wystąpił błąd
     */
    public function proxyFile(string $endpoint, string $storedName, bool $inline = false): void
    {
        $url = $this->baseUrl . '/' . ltrim($endpoint, '/') . '/file/' . rawurlencode($storedName);
        if ($inline) {
            $url .= '?inline=1';
        }

        $ch = curl_init($url);
        if ($ch === false) {
            throw new RuntimeException('Nie można zainicjować cURL');
        }

        // Strumieniowanie bezpośrednio na wyjście PHP – bez buforowania całego pliku w RAM
        $headersSent = false;
        $responseHeaders = [];

        curl_setopt_array($ch, [
            CURLOPT_TIMEOUT        => $this->timeoutSeconds,
            CURLOPT_HTTPHEADER     => ['X-API-Key: ' . $this->apiKey],
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => 0,
            // Zbierz nagłówki odpowiedzi
            CURLOPT_HEADERFUNCTION => static function ($ch, string $header) use (&$responseHeaders): int {
                $responseHeaders[] = rtrim($header, "\r\n");
                return strlen($header);
            },
            // Każdy chunk ciała trafia bezpośrednio na wyjście
            CURLOPT_WRITEFUNCTION  => static function ($ch, string $data) use (&$headersSent, &$responseHeaders): int {
                if (!$headersSent) {
                    foreach ($responseHeaders as $h) {
                        if (stripos($h, 'Content-Type:') === 0
                            || stripos($h, 'Content-Disposition:') === 0
                            || stripos($h, 'Content-Length:') === 0
                        ) {
                            header($h);
                        }
                    }
                    $headersSent = true;
                }
                echo $data;
                return strlen($data);
            },
        ]);

        curl_exec($ch);
        $httpCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $curlErr  = curl_error($ch);
        curl_close($ch);

        if ($curlErr !== '') {
            throw new RuntimeException('Błąd połączenia z serwisem plików: ' . $curlErr);
        }

        if ($httpCode === 404) {
            throw new RuntimeException('Plik nie istnieje', 404);
        }

        if ($httpCode !== 200) {
            throw new RuntimeException('Błąd pobierania pliku (HTTP ' . $httpCode . ')');
        }
    }

    /**
     * Usuń plik z serwisu (i z dysku).
     *
     * @param  string $endpoint    Endpoint routera, np. 'admin_chat'
     * @param  string $storedName  Nazwa pliku na dysku
     * @return bool  true jeśli plik istniał i został usunięty
     */
    public function deleteFile(string $endpoint, string $storedName): bool
    {
        $url = $this->baseUrl . '/' . ltrim($endpoint, '/') . '/file/' . rawurlencode($storedName);

        $ch = curl_init($url);
        if ($ch === false) {
            return false;
        }

        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_CUSTOMREQUEST  => 'DELETE',
            CURLOPT_TIMEOUT        => 10,
            CURLOPT_HTTPHEADER     => ['X-API-Key: ' . $this->apiKey],
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_SSL_VERIFYHOST => 0,
        ]);

        $response = curl_exec($ch);
        $httpCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        $data = json_decode((string)$response, true);
        return $httpCode === 200 && is_array($data) && !empty($data['success']);
    }
}

/**
 * Singleton – zwraca skonfigurowany FileApiClient.
 * Konfiguracja przez zmienne środowiskowe FILE_API_URL i FILE_API_KEY.
 */
function get_file_api_client(): FileApiClient
{
    static $instance = null;
    if ($instance === null) {
        $baseUrl = (string)(getenv('FILE_API_URL') ?: 'http://127.0.0.1:8001');
        $apiKey  = (string)(getenv('FILE_API_KEY') ?: '');
        $instance = new FileApiClient($baseUrl, $apiKey);
    }
    return $instance;
}
