<?php

namespace AiChat\Models;

use CfSettings;
use LLPhant\Chat\ChatInterface;
use SettingsHelper;
use VariablesDictionaryModel;

readonly class Text2SqlReply
{
    public function __construct(
        protected string $explanation,
        protected string $sql
    ) {
    }

    public static function fromArray(array $data): self
    {
        return new self(
            explanation: $data['explanation'] ?? '',
            sql: self::removeTrailingSemicolon($data['sql'] ?? '')
        );
    }

    public function toArray(): array
    {
        return [
            'explanation' => $this->explanation,
            'sql' => $this->sql
        ];
    }

    public static function removeTrailingSemicolon($sql): string
    {
        return rtrim($sql, ';');
    }
}

class Text2SqlModel
{
    public const DEFAULT_ATTRIBUTES = [
        ['attribute_name' => 'OS type', 'type' => 'string'],
        ['attribute_name' => 'OS kernel', 'type' => 'string'],
        ['attribute_name' => 'Solaris zone', 'type' => 'string'],
        ['attribute_name' => 'Windows roles', 'type' => 'string'],
        ['attribute_name' => 'Virtual host', 'type' => 'string'],
        ['attribute_name' => 'Highest Service Pack', 'type' => 'string'],
        ['attribute_name' => 'Highest Technology Level', 'type' => 'string'],
        ['attribute_name' => 'HA master IP', 'type' => 'slist'],
        ['attribute_name' => 'HA node state', 'type' => 'string'],
        ['attribute_name' => 'HA node state extended', 'type' => 'string'],
        ['attribute_name' => 'EC2 Region', 'type' => 'string'],
        ['attribute_name' => 'EC2 Instance Type', 'type' => 'string'],
        ['attribute_name' => 'EC2 Instance ID', 'type' => 'string'],
        ['attribute_name' => 'EC2 Image ID', 'type' => 'string'],
        ['attribute_name' => 'EC2 Availability Zone', 'type' => 'string'],
        ['attribute_name' => 'Setuid files', 'type' => 'string'],
        ['attribute_name' => 'BIOS vendor', 'type' => 'string'],
        ['attribute_name' => 'BIOS version', 'type' => 'string'],
        ['attribute_name' => 'CPU model', 'type' => 'string'],
        ['attribute_name' => 'Memory size (MB)', 'type' => 'real'],
        ['attribute_name' => 'System serial number', 'type' => 'string'],
        ['attribute_name' => 'System manufacturer', 'type' => 'string'],
        ['attribute_name' => 'System version', 'type' => 'string'],
        ['attribute_name' => 'Disk free (%)', 'type' => 'real'],
        ['attribute_name' => 'OS', 'type' => 'string'],
        ['attribute_name' => 'IPv4 addresses', 'type' => 'slist'],
        ['attribute_name' => 'Ports listening', 'type' => 'slist'],
        ['attribute_name' => 'CPU physical cores', 'type' => 'int'],
        ['attribute_name' => 'System product name', 'type' => 'string'],
        ['attribute_name' => 'Policy Release Id', 'type' => 'string'],
        ['attribute_name' => 'Physical memory (MB)', 'type' => 'int'],
        ['attribute_name' => 'System UUID', 'type' => 'string'],
        ['attribute_name' => 'Root owned setuid files', 'type' => 'string'],
        ['attribute_name' => 'CFEngine Enterprise license expiration date', 'type' => 'string'],
        ['attribute_name' => 'CFEngine Enterprise license owner', 'type' => 'string'],
        ['attribute_name' => 'Setgid files', 'type' => 'string'],
        ['attribute_name' => 'Root owned setgid files', 'type' => 'string'],
        ['attribute_name' => 'CFEngine Enterprise license file', 'type' => 'string'],
        ['attribute_name' => 'CFEngine Enterprise license utilization', 'type' => 'int'],
        ['attribute_name' => 'CFEngine Enterprise licenses allocated', 'type' => 'int'],
        ['attribute_name' => 'CFEngine Enterprise license status', 'type' => 'string'],
        ['attribute_name' => 'Policy Servers', 'type' => 'slist'],
        ['attribute_name' => 'Primary Policy Server', 'type' => 'string'],
        ['attribute_name' => 'Timezone', 'type' => 'string'],
        ['attribute_name' => 'Timezone GMT Offset', 'type' => 'string'],
        ['attribute_name' => 'Allowed users for cf-runagent', 'type' => 'slist'],
        ['attribute_name' => 'Allowed hosts for cf-runagent', 'type' => 'slist'],
        ['attribute_name' => 'CFEngine policy version', 'type' => 'string'],
        ['attribute_name' => 'Mount point', 'type' => 'slist'],
        ['attribute_name' => 'File system', 'type' => 'slist'],
        ['attribute_name' => 'Architecture', 'type' => 'string'],
        ['attribute_name' => 'CPU logical cores', 'type' => 'int'],
        ['attribute_name' => 'CFEngine ID', 'type' => 'string'],
        ['attribute_name' => 'CFEngine roles', 'type' => 'string'],
        ['attribute_name' => 'CFEngine version', 'type' => 'string'],
        ['attribute_name' => 'Uptime minutes', 'type' => 'int'],
        ['attribute_name' => 'Interfaces', 'type' => 'slist'],
        ['attribute_name' => 'MAC addresses', 'type' => 'slist'],
        ['attribute_name' => 'CPU sockets', 'type' => 'int'],
        ['attribute_name' => 'Host name', 'type' => 'string'],
        ['attribute_name' => 'Kernel', 'type' => 'string'],
        ['attribute_name' => 'Kernel Release', 'type' => 'string']
    ];

    public function __construct(
        private ChatInterface $chat
    ) {
    }

    public const TEXT_2_SQL_INSTRUCTION_PATH = __DIR__ . '/../prompts/text2sql.markdown';
    private function getPrompt(array $replacementData)
    {
        $system = str_replace(array_keys($replacementData), $replacementData, file_get_contents(self::TEXT_2_SQL_INSTRUCTION_PATH));
        $message = str_replace(
            array_keys($replacementData),
            $replacementData,
            "# User question to answer:
                    *%question%*

                    ## Previous chat history

                    ```json
                    %chat_history%
                    ```"
        );
        return ['system' => $system, 'user' => $message];
    }

    public function getReplay(string $question, ?array $history = null): Text2SqlReply
    {
        $cfSettings = CfSettings::getInstance();
        $variableDictionaryModel = new VariablesDictionaryModel();
        $promptData = $this->getPromptData($cfSettings, $variableDictionaryModel);
        if ($history) {
            $promptData['%chat_history%'] = json_encode($history);
        }
        $promptData['%question%'] = $question;
        $prompt = $this->getPrompt($promptData);
        $this->chat->setSystemMessage($prompt['system']);
        $reply = $this->chat->generateText(prompt: $prompt['user']);
        # Remove the Markdown code block markers (```json and ```)
        $reply = preg_replace('/^```json\s*|\s*```$/s', '', $reply);
        $reply = json_decode($reply, true);
        return Text2SqlReply::fromArray($reply);
    }

    private function getPromptData(CfSettings $cfSettings, VariablesDictionaryModel $variableDictionaryModel): array
    {
        $isAllowToSendAttributeNames = $cfSettings->getSetting(SettingsHelper::ALLOW_LLM_VIEW_ACCESS_TO_ATTRIBUTES_NAMES);
        $inventoryAttributres = $isAllowToSendAttributeNames ? $variableDictionaryModel->getAll() : self::DEFAULT_ATTRIBUTES;
        $inventoryData = array_reduce(
            $inventoryAttributres,
            function ($acc, $row) {
                $acc .= "- {$row['attribute_name']} / {$row['type']}" . PHP_EOL;
                return $acc;
            },
            initial: ''
        );

        return ['%attribute_names_list%' => $inventoryData];
    }
}
