<?php

namespace Modules\Transport\Services\Ai;

use App\Models\TransportAiSettingsModel;
use Throwable;

/**
 * Optional LLM-written optimization summary.
 *
 * When an OpenAI-compatible provider is configured, a compact digest of the
 * plan is sent to the chat-completions endpoint to produce a natural-language
 * summary with efficiency recommendations. Any failure (or no configuration)
 * falls back to a locally generated template summary, so plan generation is
 * never blocked by the LLM.
 */
class AiSummaryService
{
    protected const TIMEOUT_SEC = 25;

    protected array $settings;

    public function __construct(?array $settings = null)
    {
        $this->settings = $settings ?? (new TransportAiSettingsModel())->getSettings();
    }

    public function summarize(array $plan, array $routes, array $conflicts): string
    {
        if (($this->settings['llm_provider'] ?? 'none') === 'openai' && ! empty($this->settings['llm_api_key'])) {
            $summary = $this->llmSummary($plan, $routes, $conflicts);
            if ($summary !== null) {
                return $summary;
            }
        }

        return $this->templateSummary($plan, $routes, $conflicts);
    }

    protected function llmSummary(array $plan, array $routes, array $conflicts): ?string
    {
        $digest = $this->digest($plan, $routes, $conflicts);

        $baseUrl = rtrim((string) ($this->settings['llm_api_base_url'] ?: 'https://api.openai.com/v1'), '/');
        $model   = (string) ($this->settings['llm_model'] ?: 'gpt-4o-mini');

        $payload = [
            'model'    => $model,
            'messages' => [
                [
                    'role'    => 'system',
                    'content' => 'You are a fleet operations analyst. Given a JSON digest of an optimized workforce transportation plan, write a concise optimization summary (max 180 words) in plain prose: overall efficiency, vehicle utilisation, notable savings, then 2-4 concrete efficiency-improvement recommendations. Mention conflicts if present. No markdown headers, no bullet symbols other than "-".',
                ],
                [
                    'role'    => 'user',
                    'content' => json_encode($digest, JSON_UNESCAPED_UNICODE),
                ],
            ],
            'temperature' => 0.3,
            'max_tokens'  => 400,
        ];

        try {
            $client = service('curlrequest', [
                'timeout'         => self::TIMEOUT_SEC,
                'connect_timeout' => 8,
                'http_errors'     => false,
            ]);
            $response = $client->post($baseUrl . '/chat/completions', [
                'headers' => [
                    'Authorization' => 'Bearer ' . $this->settings['llm_api_key'],
                    'Content-Type'  => 'application/json',
                    'Accept'        => 'application/json',
                ],
                'body' => json_encode($payload),
            ]);

            if ($response->getStatusCode() !== 200) {
                log_message('warning', 'Transport AI: LLM summary request returned HTTP ' . $response->getStatusCode());

                return null;
            }

            $body    = json_decode((string) $response->getBody(), true);
            $content = trim((string) ($body['choices'][0]['message']['content'] ?? ''));

            return $content !== '' ? $content : null;
        } catch (Throwable $e) {
            log_message('warning', 'Transport AI: LLM summary failed: ' . $e->getMessage());

            return null;
        }
    }

    protected function digest(array $plan, array $routes, array $conflicts): array
    {
        $routeDigest = [];
        foreach (array_slice($routes, 0, 40) as $route) {
            $routeDigest[] = [
                'name'        => $route['route_name'],
                'date'        => $route['service_date'],
                'passengers'  => $route['employee_count'],
                'vehicle'     => $route['vehicle']['registration_number'] ?? null,
                'capacity'    => isset($route['vehicle']['capacity']) ? (int) $route['vehicle']['capacity'] : null,
                'driver'      => $route['driver_name'] ?? null,
                'distance_km' => $route['total_distance_km'],
                'minutes'     => $route['estimated_duration_minutes'],
                'fuel_l'      => $route['estimated_fuel_litres'],
                'cost'        => $route['estimated_cost'],
                'departure'   => $route['departure_time'],
                'arrival'     => $route['arrival_time'],
                'shift_start' => $route['shift_start_time'],
            ];
        }

        return [
            'period'      => [$plan['plan_start_date'], $plan['plan_end_date']],
            'totals'      => [
                'routes'      => $plan['total_routes'],
                'employees'   => $plan['total_employees'],
                'distance_km' => $plan['total_distance_km'],
                'minutes'     => $plan['total_duration_minutes'],
                'fuel_l'      => $plan['total_fuel_litres'],
                'cost'        => $plan['total_cost'],
            ],
            'routes'    => $routeDigest,
            'conflicts' => array_map(
                static fn (array $c): string => '[' . $c['severity'] . '] ' . $c['message'],
                array_slice($conflicts, 0, 20)
            ),
        ];
    }

    protected function templateSummary(array $plan, array $routes, array $conflicts): string
    {
        $vehicleIds = [];
        $utilisation = [];
        $savedKm = 0.0;
        foreach ($routes as $route) {
            if ($route['vehicle_id'] !== null) {
                $vehicleIds[$route['vehicle_id']] = true;
                if (! empty($route['vehicle']['capacity'])) {
                    $utilisation[] = $route['employee_count'] / (int) $route['vehicle']['capacity'];
                }
            }
            if (preg_match('/saved ~([\d.]+) km/', (string) ($route['optimization_notes'] ?? ''), $m)) {
                $savedKm += (float) $m[1];
            }
        }

        $avgUtilisation = $utilisation !== [] ? (int) round(array_sum($utilisation) / count($utilisation) * 100) : 0;
        $errors   = count(array_filter($conflicts, static fn (array $c): bool => $c['severity'] === 'error'));
        $warnings = count($conflicts) - $errors;

        $lines = [];
        $lines[] = sprintf(
            'This plan covers %s to %s with %d optimized route%s transporting %d assigned staff member%s using %d vehicle%s.',
            $plan['plan_start_date'],
            $plan['plan_end_date'],
            (int) $plan['total_routes'],
            (int) $plan['total_routes'] === 1 ? '' : 's',
            (int) $plan['total_employees'],
            (int) $plan['total_employees'] === 1 ? '' : 's',
            count($vehicleIds),
            count($vehicleIds) === 1 ? '' : 's'
        );
        $lines[] = sprintf(
            'Estimated totals: %.1f km driven, %d minutes of travel, %.1f litres of fuel and a transportation cost of %.3f. Average seat utilisation is %d%%.',
            (float) $plan['total_distance_km'],
            (int) $plan['total_duration_minutes'],
            (float) $plan['total_fuel_litres'],
            (float) $plan['total_cost'],
            $avgUtilisation
        );

        if ($savedKm > 0) {
            $lines[] = sprintf('Routing refinements saved approximately %.1f km compared with unoptimized paths.', $savedKm);
        }

        if ($errors > 0 || $warnings > 0) {
            $lines[] = sprintf('%d conflict%s and %d warning%s were detected; review the conflicts panel and apply the recommended replacements before approval.', $errors, $errors === 1 ? '' : 's', $warnings, $warnings === 1 ? '' : 's');
        } else {
            $lines[] = 'No scheduling conflicts were detected; all routes satisfy capacity, licensing, availability and shift-arrival rules.';
        }

        if ($avgUtilisation > 0 && $avgUtilisation < 60) {
            $lines[] = 'Recommendation: seat utilisation is below 60% — consider consolidating site runs or assigning smaller vehicles to reduce fuel consumption.';
        }

        return implode(' ', $lines);
    }
}
