<?php
namespace boru\boruai\Openai\Models;

use boru\boruai\Openai\OpenAIChat;
use boru\boruai\Openai\OpenAIMessage;

class MessagesCompressor {
    private $originalTotal;
    private $originalMessages = [];
    private $startMessages = [];
    private $endMessages = [];
    private $messages = [];
    private $total = 0;
    private $excludeLastN = 1;
    private $startAt = 3;

    public function __construct($messages = [], $excludeLastN = 1, $startAt = 3) {
        if($messages instanceof Messages) {
            $messages = $messages->messages();
        }
        $this->originalMessages = $messages;
        $this->excludeLastN = $excludeLastN;
        $this->startAt = $startAt;
        $this->originalTotal = count($messages);
        if($this->originalTotal > 0) {
            $this->sanitizeCounts($this->startAt,$this->excludeLastN,$this->originalTotal);
        }
        if($this->originalTotal > 0) {
            $this->startMessages = array_slice($this->originalMessages,0,$this->startAt);
        }
        if($this->originalTotal > 0) {
            $this->endMessages = array_slice($this->originalMessages,-$this->excludeLastN);
        }
        $this->originalMessages = array_slice($this->originalMessages,$this->startAt,-$this->excludeLastN);
    }

    public function originalTotal() {
        return $this->originalTotal;
    }
    public function originalMessages() {
        return $this->originalMessages;
    }
    public function startMessages() {
        return $this->startMessages;
    }
    public function endMessages() {
        return $this->endMessages;
    }
    public function startAt() {
        return $this->startAt;
    }
    public function excludeLastN() {
        return $this->excludeLastN;
    }
    public function messages() {
        return $this->messages;
    }
    public function total() {
        return $this->total;
    }

    public function compress() {
        $summary = $this->getSummary();
        $summaryText = "Summary of previous messages:\n";
        $summaryText .= json_encode($summary, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE);
        $message = new OpenAIMessage([
            "role" => "assistant",
            "content" => $summaryText
        ]);
        $this->messages = array_merge($this->startMessages,[$message],$this->endMessages);
        $this->total = count($this->messages);
        return $this->messages;
    }

    public function getSummary() {
        $chat = new OpenAIChat();
        $prompt = $this->prompt();
        $chat->addMessage("user", $prompt);
        $summary = $chat->json(true);
        return $summary;
    }

    public function prompt() {
        $conversationContext = "";
        $convoParts = [];
        $summaryArray = $this->summaryArray();
        foreach($summaryArray as $message) {
            if($message["role"] == "user") {
                $convoParts[] = "User: ".$message["content"];
            } elseif($message["role"] == "assistant") {
                $convoParts[] = "Assistant: ".$message["content"];
            }
            if(isset($message["functions"]) && is_array($message["functions"])) {
                foreach($message["functions"] as $function) {
                    $convoParts[] = "FunctionCall: ".$function["name"]."(".$function["arguments"].")";
                    if(isset($function["response"]) && is_string($function["response"])) {
                        $convoParts[] = "FunctionResult: ".$function["response"];
                    }
                }
            }
        }
        $conversationContext = implode("\n",$convoParts);
        $conversationContext = str_replace("\n\n","\n",$conversationContext);
        $prompt = str_replace("{{conversationSummary}}",$conversationContext,self::$prompt);
        return $prompt;
    }

    public function context() {
        //foreach($this->message)
    }

    public function summaryArray() {
        $toolCallMap = [];
        $messages = $this->originalMessages;
        if(empty($messages)) {
            return [];
        }
        foreach($messages as $i=>$message) {
            if($message instanceof OpenAIMessage) {
                $messages[$i] = $message->toArray();
                $message = $messages[$i];
            }
            if(isset($message["tool_calls"]) && is_array($message["tool_calls"])) {
                foreach($message["tool_calls"] as $ti => $toolCall) {
                    $toolCallMap[$toolCall["id"]] = $i;
                    if(isset($toolCall["function"])) {
                        $messages[$i]['role'] = 'function';
                        $messages[$i]["functions"][$toolCall["id"]] = [
                            "name" => $toolCall["function"]["name"],
                            "arguments" => $toolCall["function"]["arguments"],
                            "response" => ""
                        ];
                        unset($messages[$i]["tool_calls"][$ti]);
                    }
                }
                if(empty($messages[$i]["tool_calls"])) { unset($messages[$i]["tool_calls"]); }
                if(empty($messages[$i]['content'])) { unset($messages[$i]['content']); }
            }
        }
        foreach($messages as $i=>$message) {
            if(isset($message["tool_call_id"]) && is_string($message["tool_call_id"])) {
                if(isset($toolCallMap[$message["tool_call_id"]])) {
                    $tcId = $message["tool_call_id"];
                    if(!isset($toolCallMap[$tcId])) {
                        continue;
                    }
                    $index = $toolCallMap[$tcId];
                    $messages[$index]["functions"][$tcId]["response"] = $message["content"];
                    unset($messages[$i]);
                }
            }
        }

        return $messages;
    }

    private function sanitizeCounts(&$startAt,&$excludeLastN,$total=10) {
        if($startAt < 0) {
            $startAt = 0;
        }
        if($excludeLastN < 0) {
            $excludeLastN = 0;
        }
        if($startAt > $total) {
            $startAt = $total;
        }
        if($excludeLastN > $total) {
            $excludeLastN = $total;
        }
        if($startAt + $excludeLastN > $total) {
            $excludeLastN = $total - $startAt;
        }
        if($excludeLastN == 0 && $startAt == 0) {
            $excludeLastN = $total;
        }
        if($excludeLastN > $total) {
            $excludeLastN = $total;
        }
        if($startAt > $total) {
            $startAt = $total;
        }
        if($startAt + $excludeLastN >= $total) {
            $tooLong = true;
            while($startAt > 0 && $tooLong) {
                $startAt--;
                if($startAt + $excludeLastN < $total) {
                    $tooLong = false;
                }
            }
            while($excludeLastN > 0 && $tooLong) {
                $excludeLastN--;
                if($startAt + $excludeLastN < $total) {
                    $tooLong = false;
                }
            }
        }
    }

    private static $prompt = 'You are an assistant that deeply analyzes long conversations.

Here is the conversation history:
"""
{{conversationSummary}}
"""

Your tasks are:

1. **Group** the conversation into sections by topic. When the conversation noticeably shifts topic or purpose, start a new section.

2. For each section:
  - Create a short title describing the section topic.
  - Extract key facts from that section (facts should include user intentions, requests, important items, preferences, etc).
  - Summarize the section briefly (2-4 sentences).

3. After processing all sections, create an **overall summary** of the full conversation in 3-6 sentences.

Output ONLY in this exact JSON format:

{
  "sections": [
    {
      "title": "short title here",
      "key_facts": {
        "intent": "string or null",
        "facts": ["fact1", "fact2", ...],
        "preferences": ["preference1", "preference2", ...],
        "additional_info": "string or null"
      },
      "summary": "summary of this section."
    },
    {... more sections ...}
  ],
  "overall_summary": "overall summary here"
}

If a field is not relevant, set it to null or an empty array.  
Be concise. Do NOT add anything outside the JSON.';
}