<?php

namespace App\Services\LlmPrediction;

use App\Enums\PredictionSource;
use App\Enums\TrendDirection;
use App\Models\BrentPrice;
use App\Models\PricePrediction;
use App\Services\ApiLogger;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Throwable;

class OpenAiPredictionProvider implements OilPredictionProvider
{
    private const int LLM_MAX_CONFIDENCE = 85;

    public function __construct(
        private readonly ApiLogger $apiLogger,
    ) {}

    public function predict(Collection $prices): ?PricePrediction
    {
        if (! config('services.openai.api_key')) {
            return null;
        }

        $priceList = $prices->sortBy('date')
            ->map(fn (BrentPrice $p) => $p->date->toDateString().': $'.$p->price_usd)
            ->implode("\n");

        $url = 'https://api.openai.com/v1/chat/completions';

        try {
            $response = $this->apiLogger->send('openai', 'POST', $url, fn () => Http::timeout(15)
                ->withToken(config('services.openai.api_key'))
                ->post($url, [
                    'model' => config('services.openai.model', 'gpt-4o-mini'),
                    'response_format' => [
                        'type' => 'json_schema',
                        'json_schema' => [
                            'name' => 'oil_prediction',
                            'strict' => true,
                            'schema' => [
                                'type' => 'object',
                                'properties' => [
                                    'direction' => ['type' => 'string', 'enum' => ['rising', 'falling', 'flat']],
                                    'confidence' => ['type' => 'integer'],
                                    'reasoning' => ['type' => 'string'],
                                ],
                                'required' => ['direction', 'confidence', 'reasoning'],
                                'additionalProperties' => false,
                            ],
                        ],
                    ],
                    'messages' => [[
                        'role' => 'user',
                        'content' => $this->prompt($priceList),
                    ]],
                ]));

            if (! $response->successful()) {
                Log::error('OpenAiPredictionProvider: request failed', ['status' => $response->status()]);

                return null;
            }

            $data = json_decode($response->json('choices.0.message.content') ?? '{}', true);

            if (! isset($data['direction'], $data['confidence'], $data['reasoning'])) {
                Log::error('OpenAiPredictionProvider: unexpected response format', ['data' => $data]);

                return null;
            }

            $direction = TrendDirection::tryFrom($data['direction']);

            if ($direction === null) {
                Log::error('OpenAiPredictionProvider: invalid direction', ['direction' => $data['direction']]);

                return null;
            }

            return new PricePrediction([
                'predicted_for' => now()->toDateString(),
                'source' => PredictionSource::Llm,
                'direction' => $direction,
                'confidence' => min((int) $data['confidence'], self::LLM_MAX_CONFIDENCE),
                'reasoning' => $data['reasoning'],
                'generated_at' => now(),
            ]);
        } catch (Throwable $e) {
            Log::error('OpenAiPredictionProvider: predict failed', ['error' => $e->getMessage()]);

            return null;
        }
    }

    private function prompt(string $priceList): string
    {
        return <<<PROMPT
            You are analyzing Brent crude oil price data for a UK fuel price alert service.
            Predict the short-term direction over the next 3–5 days.

            Recent Brent crude prices (USD/barrel):
            {$priceList}

            Respond with direction (rising, falling, or flat), a confidence score (0–85),
            and a one-sentence reasoning.
            PROMPT;
    }
}
