<?php

namespace platyFramework;

require_once(__DIR__ . "/../ptycommon/model.php");

/**
 * Ollama API Model
 * Ollama API를 사용하기 위한 클래스 (OpenAI 호환 API)
 */
class OllamaAPIModel extends model
{
    private $apiUrl = 'http://localhost:11434';
    private $model = 'llama3';
    private $maxTokens = 4096;
    private $temperature = 1.0;
    private $conversationHistory = [];
    private $debug = false;

    /**
     * API URL 설정
     * @param string $apiUrl Ollama 서버 URL
     * @return $this
     */
    public function setApiUrl($apiUrl)
    {
        $this->apiUrl = rtrim($apiUrl, '/');
        return $this;
    }

    /**
     * 모델 설정
     * @param string $model 사용할 모델명 (llama3, mistral, codellama 등)
     * @return $this
     */
    public function setModel($model)
    {
        $this->model = $model;
        return $this;
    }

    /**
     * 최대 토큰 수 설정
     * @param int $maxTokens
     * @return $this
     */
    public function setMaxTokens($maxTokens)
    {
        $this->maxTokens = $maxTokens;
        return $this;
    }

    /**
     * Temperature 설정
     * @param float $temperature 0.0 ~ 2.0
     * @return $this
     */
    public function setTemperature($temperature)
    {
        $this->temperature = $temperature;
        return $this;
    }

    /**
     * 대화 기록 초기화
     * @return $this
     */
    public function resetConversation()
    {
        $this->conversationHistory = [];
        return $this;
    }

    /**
     * 디버그 모드 설정
     * @param bool $debug 디버그 모드 활성화 여부
     * @return $this
     */
    public function setDebug($debug)
    {
        $this->debug = $debug;
        return $this;
    }

    /**
     * Ollama API 호출
     * @param string $message 사용자 메시지
     * @param bool $keepHistory 대화 기록 유지 여부
     * @return array|false 응답 배열 또는 false
     */
    public function get($message, $keepHistory = false)
    {
        // 현재 메시지를 대화 기록에 추가
        $messages = $this->conversationHistory;
        $messages[] = [
            'role' => 'user',
            'content' => $message
        ];

        // API 요청 데이터 구성 (OpenAI 호환 형식)
        $data = [
            'model' => $this->model,
            'messages' => $messages,
            'options' => [
                'num_predict' => $this->maxTokens,
                'temperature' => $this->temperature,
            ],
            'stream' => false,
        ];

        // API 호출
        $response = $this->_sendRequest($data);

        if ($response === false) {
            return false;
        }

        // 대화 기록 유지
        if ($keepHistory && isset($response['message']['content'])) {
            $this->conversationHistory = $messages;
            $this->conversationHistory[] = [
                'role' => 'assistant',
                'content' => $response['message']['content']
            ];
        }

        return $response;
    }

    /**
     * 시스템 프롬프트와 함께 메시지 전송
     * @param string $message 사용자 메시지
     * @param string $systemPrompt 시스템 프롬프트
     * @return array|false
     */
    public function getWithSystem($message, $systemPrompt)
    {
        $messages = [
            [
                'role' => 'system',
                'content' => $systemPrompt
            ],
            [
                'role' => 'user',
                'content' => $message
            ]
        ];

        $data = [
            'model' => $this->model,
            'messages' => $messages,
            'options' => [
                'num_predict' => $this->maxTokens,
                'temperature' => $this->temperature,
            ],
            'stream' => false,
        ];

        return $this->_sendRequest($data);
    }

    /**
     * API 요청 전송
     * @param array $data 요청 데이터
     * @return array|false
     */
    private function _sendRequest($data)
    {
        // API 호출 타임아웃 방지
        set_time_limit(0);
        ini_set('max_execution_time', '0');

        $url = $this->apiUrl . '/api/chat';
        $ch = curl_init($url);

        $headers = [
            'Content-Type: application/json'
        ];

        $jsonData = json_encode($data);

        // 디버그 출력
        if ($this->debug) {
            fwrite(STDERR, "━━━━━━━━━━━━ CURL REQUEST ━━━━━━━━━━━━\n");
            fwrite(STDERR, "URL: {$url}\n");
            fwrite(STDERR, "Method: POST\n");
            fwrite(STDERR, "Headers:\n");
            foreach ($headers as $header) {
                fwrite(STDERR, "  {$header}\n");
            }
            fwrite(STDERR, "Body:\n");
            fwrite(STDERR, "  " . json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n");
            fwrite(STDERR, "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
        }

        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_TIMEOUT, 300); // 5분 타임아웃

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);

        // 디버그 출력 (응답)
        if ($this->debug) {
            fwrite(STDERR, "━━━━━━━━━━━━ CURL RESPONSE ━━━━━━━━━━━━\n");
            fwrite(STDERR, "HTTP Code: {$httpCode}\n");
            if ($error) {
                fwrite(STDERR, "cURL Error: {$error}\n");
            }
            fwrite(STDERR, "Response:\n");
            $prettyResponse = json_decode($response, true);
            if ($prettyResponse) {
                fwrite(STDERR, "  " . json_encode($prettyResponse, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n");
            } else {
                fwrite(STDERR, "  {$response}\n");
            }
            fwrite(STDERR, "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
        }

        if ($error) {
            error_log("Ollama API cURL Error: " . $error);
            return false;
        }

        if ($httpCode !== 200) {
            error_log("Ollama API HTTP Error: " . $httpCode . " - " . $response);
            return false;
        }

        $result = json_decode($response, true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            error_log("Ollama API JSON Decode Error: " . json_last_error_msg());
            return false;
        }

        return $result;
    }

    /**
     * 응답에서 텍스트만 추출
     * @param array $response API 응답
     * @return string|false
     */
    public function extractText($response)
    {
        if (isset($response['message']['content'])) {
            return $response['message']['content'];
        }
        return false;
    }

    /**
     * 간단한 텍스트 응답만 받기
     * @param string $message
     * @return string|false
     */
    public function getSimple($message)
    {
        $response = $this->get($message);
        return $this->extractText($response);
    }

    /**
     * 텍스트 임베딩 API 호출
     * @param string $text 임베딩할 텍스트
     * @param string $model 임베딩 모델 (기본값: nomic-embed-text)
     * @return array|false ['embedding' => [...], 'usage' => null] 또는 false
     */
    public function getEmbedding($text, $model = null)
    {
        if ($model === null) {
            $model = 'nomic-embed-text';
        }

        $url = $this->apiUrl . '/api/embeddings';

        $data = array(
            'model' => $model,
            'prompt' => $text,
        );

        $headers = array(
            'Content-Type: application/json',
        );

        // 디버그 출력
        if ($this->debug) {
            fwrite(STDERR, "━━━━━━━━━━━━ CURL REQUEST ━━━━━━━━━━━━\n");
            fwrite(STDERR, "URL: {$url}\n");
            fwrite(STDERR, "Method: POST\n");
            fwrite(STDERR, "Headers:\n");
            foreach ($headers as $header) {
                fwrite(STDERR, "  {$header}\n");
            }
            fwrite(STDERR, "Body:\n");
            fwrite(STDERR, "  " . json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n");
            fwrite(STDERR, "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
        }

        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_TIMEOUT, 300);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $error = curl_error($ch);
        curl_close($ch);

        // 디버그 출력 (응답)
        if ($this->debug) {
            fwrite(STDERR, "━━━━━━━━━━━━ CURL RESPONSE ━━━━━━━━━━━━\n");
            fwrite(STDERR, "HTTP Code: {$httpCode}\n");
            if ($error) {
                fwrite(STDERR, "cURL Error: {$error}\n");
            }
            fwrite(STDERR, "Response:\n");
            $prettyResponse = json_decode($response, true);
            if ($prettyResponse) {
                if (isset($prettyResponse['embedding'])) {
                    $prettyResponse['embedding'] = '[' . count($prettyResponse['embedding']) . ' dimensions...]';
                }
                fwrite(STDERR, "  " . json_encode($prettyResponse, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n");
            } else {
                fwrite(STDERR, "  {$response}\n");
            }
            fwrite(STDERR, "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
        }

        if ($error) {
            error_log("Ollama Embedding API cURL Error: " . $error);
            return false;
        }

        if ($httpCode !== 200) {
            error_log("Ollama Embedding API HTTP Error: " . $httpCode . " - " . $response);
            return false;
        }

        $result = json_decode($response, true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            error_log("Ollama Embedding API JSON Decode Error: " . json_last_error_msg());
            return false;
        }

        if (!isset($result['embedding'])) {
            return false;
        }

        return array(
            'response' => $result,
            'embedding' => $result['embedding'],
            'usage' => null,  // Ollama doesn't return token usage for embeddings
            '_raw' => $result,
        );
    }
}
