<?php

namespace AiChat\Controllers;

use AiChat\Models\LlmFactory;
use AiChat\Models\Text2SqlModel;
use AiChat\Services\LlmErrorResponseDecorator;
use CfLLMConfig;
use Exception;
use LLMConfigEntity;
use Response;
use ResponseException;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Validation;

/**
 * @uri /ai/text-to-sql
 */
class TextToSqlResource extends \CfProtectedResource
{
    use \ValidatesRequests;

    /**
     * @rbacName Send message to AI chat
     * @rbacGroup AI Chat
     * @rbacAlias ai.chat.message
     * @rbacAllowedByDefault
     */
    public function post($request): Response
    {
        $requestData = \Utils::getValidJsonData($request->data, returnArray: true);
        self::validateRequest($requestData);

        $response = new Response($request);
        $cfLLMConfig =  new CfLLMConfig(\CfSettings::getInstance()->getConnection());

        $configs = $cfLLMConfig->list();

        if (!$configs) {
            throw new ResponseException(
                'Add an LLM configuration before using the chat.',
                Response::UNPROCESSABLE_ENTITY
            );
        }

        /**
         * @var LLMConfigEntity
         */
        $llmConfig = $configs[0];
        $chat = LlmFactory::create($llmConfig);

        $text2sql = new Text2SqlModel($chat);
        try {
            $reply = $text2sql->getReplay($requestData['question'], $requestData['history'] ?? null);
        } catch (Exception $exception) {
            $response->code = Response::BADREQUEST;
            // Decorate LLM provider errors with unified format
            if (LlmErrorResponseDecorator::shouldDecorate($exception)) {
                $errorResponse = LlmErrorResponseDecorator::decorateError($exception, $llmConfig->provider);
                $response->body = json_encode($errorResponse);
            } else {
                $response->body = $exception->getMessage();
            }
            return $response;
        }

        $return = $reply->toArray();
        $return['meta'] = ['provider' => $llmConfig->provider, 'model' => $llmConfig->model];

        $response->body = json_encode(value: $return);
        return $response;
    }

    private static function validateRequest(array $requestData)
    {
        $validator = Validation::createValidator();
        $constraints = new Assert\Collection(
            fields: [
                'question' => new Assert\Required([
                    new Assert\Type(type: 'string'),
                    new Assert\Length(max: MAX_VALUE_LENGTH)
                ]),
                'history' => new Assert\Optional([
                    new Assert\Type(type: 'array')
                ])
            ]
        );

        self::throwOnIssues($validator->validate($requestData, $constraints));
    }
}
