<?php

// +-------------------------------------------------+
// © 2002-2004 PMB Services / www.sigb.net pmb@sigb.net et contributeurs (voir www.sigb.net)
// +-------------------------------------------------+
// $Id: LeChat.php,v 1.1.2.8.2.4 2026/02/13 12:44:17 gneveu Exp $

namespace Pmb\AI\Library\Services\Mistral;

if (stristr($_SERVER['REQUEST_URI'], '/'.basename(__FILE__))) {
    die("no access");
}

use encoding_normalize;
use Pmb\AI\Library\Services\Agent;
use Pmb\AI\Library\Services\AgentUtils;

class LeChat implements Agent
{
    use AgentUtils;

    /**
     * Url utilisee pour la conversation
     */
    public const URL_COMPLETION = "https://api.mistral.ai/v1/chat/completions";

    /**
     * Url utilisee pour le calcul des embeddings
     */
    public const URL_EMBEDDINGS = "https://api.mistral.ai/v1/embeddings";

    /**
     * Url utilisee pour la recuperation des models
     */
    public const URL_MODELS = "https://api.mistral.ai/v1/models";

    /**
     * Parameters
     */
    private $parameters;

    /**
     * Set parameters
     *
     * @param array $parameters
     * @return void
     */
    public function setParameters(array $parameters): void
    {
        $this->parameters = $parameters;
    }

    /**
     * Get parameters
     *
     * @return array
     */
    public function getParameters(): array
    {
        return $this->parameters;
    }

    /**
     * Convert text to embeddings
     *
     * @param string|array  $text
     * @return array
     */
    public function textToEmbeddings(string|array  $text): array
    {
        $embeddingSend = [
            "input" => $text,
            "model" => "mistral-embed",
            "encoding_format" => "float"
        ];

        $response = $this->post(self::URL_EMBEDDINGS, $this->headers(), encoding_normalize::json_encode($embeddingSend));
        if (($response instanceof \CurlResponse) && $response->headers['Status-Code'] == 200) {
            $response = json_decode($response->body, true);
            return array_map(fn ($data) => $data["embedding"], $response["data"]);
        }
        return [];
    }

    /**
     * Chat
     *
     * @param string $promptSystem
     * @param string $promptUser
     * @return string
     */
    public function chat(string $promptSystem, string $promptUser, bool $jsonResponse = false): string
    {
        $data = [
            "model" => $this->parameters['model'],
            "messages" => [
                [ "role" => "system", "content" => $promptSystem ],
                [ "role" => "user", "content" => $promptUser ]
            ],
            "stream" => false,
        ];
        
        if ($jsonResponse) {
            $data["response_format"] = [
                "type" => "json_object"
            ];
        }

        $options = $this->options();
        if (isset($options['temperature'])) {
            $data['temperature'] = $options['temperature'];
        }

        $response = $this->post(self::URL_COMPLETION, $this->headers(), encoding_normalize::json_encode($data));
        if (($response instanceof \CurlResponse) && $response->headers['Status-Code'] == 200) {
            $response = encoding_normalize::json_decode($response->body, true);
            return $response["choices"][0]["message"]["content"];
        }
        return "";
    }

    /**
     * Permet de recuperer la liste des models
     *
     * @param array $parameters
     * @return array
     */
    public function getModels(array $parameters): array
    {
        $response = $this->get(self::URL_MODELS, $this->computeHeaders($parameters['auth_token'] ?? $parameters['token']));
        $listModels = [];
        if (($response instanceof \CurlResponse) &&  $response->headers['Status-Code'] == 200) {
            $models = encoding_normalize::json_decode($response->body, true);
            foreach ($models["data"] as $model) {
                if (!in_array($model["name"], $listModels)) {
                    $listModels[$model["id"]] = $model["name"];
                }
            }
        }
        return $listModels;
    }

    /**
     * Permet de tester la validité du token
     *
     * @param array $parameters
     * @return boolean
     */
    public function validateToken(array $parameters): bool
    {
        $response = $this->get(self::URL_MODELS, $this->computeHeaders($parameters['auth_token'] ?? $parameters['token']));
        if (($response instanceof \CurlResponse) && $response->headers['Status-Code'] != "200") {
            return false;
        }
        return true;
    }

    /**
     * Vérification des paramètres
     *
     * @param array $parameters
     * @return boolean
     */
    public function checkParameters(array $parameters): bool
    {
        if (isset($parameters['options']['temperature']) && !(is_numeric($parameters['options']['temperature']) || $parameters['options']['temperature'] === "")) {
            return false;
        }

        if (isset($parameters['auth_token']) && isset($parameters['model'])) {
            $models = $this->getModels($parameters);
            if (in_array($parameters['model'], $models, true)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Compute headers
     *
     * @param string $token
     * @return array
     */
    private function computeHeaders(string $token): array
    {
        $headers = [];
        $headers['Content-Type'] = 'application/json';
        $headers['Accept'] = 'application/json';
        $headers['Authorization'] = 'Bearer ' . $token;

        return $headers;
    }

    /**
     * Get headers
     *
     * @return array
     */
    private function headers(): array
    {
        return $this->computeHeaders($this->parameters['auth_token']);
    }

    /**
     * Get options
     *
     * @return array
     */
    private function options(): array
    {
        $options = [];
        if (isset($this->parameters['options']['temperature']) && is_numeric($this->parameters['options']['temperature'])) {
            $options['temperature'] = $this->parameters['options']['temperature'];
        }

        return $options;
    }
}
