<?php
namespace App\Services;

use App\Repositories\ConfigRepository;
use App\Utils\Logger;
use RuntimeException;

class OpenAiService
{
    private ConfigRepository $config;
    private array $fileConfig;

    public function __construct(ConfigRepository $config)
    {
        $this->config = $config;
        $path = __DIR__ . '/../config/openai.php';
        $loaded = is_file($path) ? require $path : [];
        $this->fileConfig = is_array($loaded) ? $loaded : [];
    }

    public function isConfigured(): bool
    {
        return $this->apiKey() !== '';
    }

    public function isEnabled(): bool
    {
        $default = $this->fileConfig['enabled'] ?? (defined('OPENAI_FEATURE_ENABLED') ? OPENAI_FEATURE_ENABLED : '0');
        $value = $this->config->getValue('ai_feature_enabled', $default);
        return $this->normalizeFlag($value);
    }

    public function model(): string
    {
        $default = $this->fileConfig['model'] ?? (defined('OPENAI_MODEL') ? OPENAI_MODEL : 'gpt-4.1-mini');
        $model = trim((string)$this->config->getValue('openai_model', $default));
        return $model !== '' ? $model : 'gpt-4.1-mini';
    }

    public function createStructuredResponse(string $systemPrompt, array $context, array $schema): array
    {
        $apiKey = $this->apiKey();
        if ($apiKey === '') {
            throw new RuntimeException('OpenAI API key is not configured');
        }

        $body = [
            'model' => $this->model(),
            'input' => [
                [
                    'role' => 'system',
                    'content' => [
                        ['type' => 'input_text', 'text' => $systemPrompt],
                    ],
                ],
                [
                    'role' => 'user',
                    'content' => [
                        ['type' => 'input_text', 'text' => json_encode($context, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)],
                    ],
                ],
            ],
            'text' => [
                'format' => [
                    'type' => 'json_schema',
                    'name' => (string)($schema['name'] ?? 'ai_response'),
                    'schema' => $schema['schema'] ?? ['type' => 'object'],
                    'strict' => (bool)($schema['strict'] ?? true),
                ],
            ],
        ];

        $response = $this->request('/responses', $body, $apiKey);
        $text = $this->extractOutputText($response);
        $decoded = json_decode($text, true);
        if (!is_array($decoded)) {
            Logger::logWarning('OpenAI returned non-JSON output', [
                'model' => $this->model(),
                'output_preview' => function_exists('mb_substr') ? mb_substr($text, 0, 500) : substr($text, 0, 500),
            ]);
            throw new RuntimeException('AI response was not valid JSON');
        }

        return $decoded;
    }

    private function request(string $path, array $body, string $apiKey): array
    {
        $url = rtrim($this->apiBase(), '/') . $path;
        $payload = json_encode($body, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        if ($payload === false) {
            throw new RuntimeException('Failed to encode AI request');
        }

        $headers = [
            'Authorization: Bearer ' . $apiKey,
            'Content-Type: application/json',
        ];

        if (function_exists('curl_init')) {
            $ch = curl_init($url);
            curl_setopt_array($ch, [
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_POST => true,
                CURLOPT_POSTFIELDS => $payload,
                CURLOPT_HTTPHEADER => $headers,
                CURLOPT_TIMEOUT => $this->timeout(),
            ]);
            $raw = curl_exec($ch);
            $httpCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
            $curlError = curl_error($ch);
            curl_close($ch);

            if ($raw === false) {
                throw new RuntimeException('AI request failed: ' . $curlError);
            }
            return $this->decodeHttpResponse((string)$raw, $httpCode);
        }

        $context = stream_context_create([
            'http' => [
                'method' => 'POST',
                'header' => implode("\r\n", $headers),
                'content' => $payload,
                'timeout' => $this->timeout(),
                'ignore_errors' => true,
            ],
        ]);
        $raw = @file_get_contents($url, false, $context);
        $statusLine = $http_response_header[0] ?? '';
        preg_match('/\s(\d{3})\s/', $statusLine, $matches);
        $httpCode = isset($matches[1]) ? (int)$matches[1] : 0;
        if ($raw === false) {
            throw new RuntimeException('AI request failed');
        }
        return $this->decodeHttpResponse((string)$raw, $httpCode);
    }

    private function decodeHttpResponse(string $raw, int $httpCode): array
    {
        $decoded = json_decode($raw, true);
        if (!is_array($decoded)) {
            throw new RuntimeException('AI API returned an invalid response');
        }
        if ($httpCode < 200 || $httpCode >= 300) {
            $message = (string)($decoded['error']['message'] ?? 'AI API request failed');
            throw new RuntimeException('AI API error (' . $httpCode . '): ' . $message);
        }
        return $decoded;
    }

    private function extractOutputText(array $response): string
    {
        if (isset($response['output_text']) && trim((string)$response['output_text']) !== '') {
            return (string)$response['output_text'];
        }

        foreach ((array)($response['output'] ?? []) as $item) {
            foreach ((array)($item['content'] ?? []) as $content) {
                if (isset($content['text']) && trim((string)$content['text']) !== '') {
                    return (string)$content['text'];
                }
            }
        }

        throw new RuntimeException('AI API returned no text output');
    }

    private function apiKey(): string
    {
        $env = trim((string)(getenv('OPENAI_API_KEY') ?: ''));
        if ($env !== '') {
            return $env;
        }
        if (defined('OPENAI_API_KEY') && trim((string)OPENAI_API_KEY) !== '') {
            return trim((string)OPENAI_API_KEY);
        }
        $fileKey = trim((string)($this->fileConfig['api_key'] ?? ''));
        if ($fileKey !== '') {
            return $fileKey;
        }
        return trim((string)$this->config->getValue('openai_api_key', ''));
    }

    private function apiBase(): string
    {
        $default = $this->fileConfig['api_base'] ?? (defined('OPENAI_API_BASE') ? OPENAI_API_BASE : 'https://api.openai.com/v1');
        $base = trim((string)$this->config->getValue('openai_api_base', $default));
        return $base !== '' ? $base : 'https://api.openai.com/v1';
    }

    private function timeout(): int
    {
        $default = $this->fileConfig['timeout'] ?? (defined('OPENAI_TIMEOUT') ? OPENAI_TIMEOUT : 30);
        $timeout = (int)$this->config->getValue('openai_timeout', $default);
        return max(5, min(120, $timeout));
    }

    private function normalizeFlag($value): bool
    {
        $normalized = is_string($value) ? strtolower(trim($value)) : $value;
        return in_array($normalized, [1, '1', true, 'true', 'on', 'yes'], true);
    }
}
