<?php
namespace boru\boruai\OCR\Agents;

use boru\boruai\BoruAI;
use boru\boruai\Models\File;
use boru\boruai\Models\Functions\FunctionBase;
use boru\boruai\Models\Response;
use boru\boruai\Models\ToolDefinition;
use boru\boruai\Openai\OpenAI;
use boru\output\Output;

class OCRAgent {
    public static $maxRetries = 3;
    public static $retryDelay = 5;

    private static $fileIDS = [];

    private $reference = null;
    private $pdfFilePath = null;
    private $fileId = null;

    private $retries = 0;

    private $instructions = "";
    private $message = "";

    private $tools = [];

    private $chat;

    /**
     * OCRProcessor constructor.
     * @param string $pdfPath Path to the PDF file to be processed.
     * @param string $reference Optional reference for the OCR process.
     */
    public function __construct($pdfPath,$reference="") {
        $this->reference = $reference;
        $this->pdfFilePath = $pdfPath;
        //ensure the file exists
        if(!is_file($pdfPath)) {
            throw new \Exception("PDF file does not exist: ".$pdfPath);
        }
    }

    public function init($model="gpt-4.1") {
        $this->chat = new Response();
        $this->chat->model($model);
        $this->chat->instructions($this->instructions());
        if(!empty($this->tools())) {
            $this->chat->addTools($this->tools());
        }
    }

    public function instructions($instructions=null) {
        if($instructions !== null) {
            $this->instructions = $instructions;
        }
        return $this->instructions;
    }

    public function addMessage($message) {
        if(!empty($message) && !is_array($message)) {
            $this->message[] = $message;
        }
        if(!is_array($this->message)) {
            $this->message = [];
        }
        if(is_array($message)) {
            foreach($message as $msg) {
                if(!empty($msg)) {
                    $this->message[] = $msg;
                }
            }
        } else {
            if(!empty($message)) {
                $this->message[] = $message;
            }
        }
        return $this->message;
    }

    public function message($message=null) {
        if($message !== null) {
            $this->message = $message;
        }
        return $this->message;
    }

    public function tools($tools=null) {
        if($tools !== null) {
            if(!is_array($tools)) {
                $tools = [$tools];
            }
            $this->tools = $tools;
        }
        return $this->tools;
    }

    public function fileId() {
        if($this->fileId) {
            return $this->fileId;
        }
        if(isset(static::$fileIDS[$this->pdfFilePath])) {
            $this->fileId = static::$fileIDS[$this->pdfFilePath];
            return $this->fileId;
        }
        $fileIdFile = $this->pdfFilePath . ".fileid";
        if(file_exists($fileIdFile)) {
            $this->fileId = trim(file_get_contents($fileIdFile));
            static::$fileIDS[$this->pdfFilePath] = $this->fileId;
            return $this->fileId;
        }
        $file = File::uploadFile($this->pdfFilePath, "user_data");
        Output::outLine("Uploaded file: ".$this->pdfFilePath);
        if(!$file) {
            throw new \Exception("Failed to upload file: ".$this->pdfFilePath);
        }
        $this->fileId = $file->fileId();
        static::$fileIDS[$this->pdfFilePath] = $this->fileId;
        file_put_contents($fileIdFile, $this->fileId);
        return $file->fileId();
    }

    private function reTryOrReturn($result) {
        if($this->retries < static::$maxRetries) {
            $this->retries++;
            Output::outLine("[OCR] Retrying OCR process for file: ".$this->pdfFilePath." (Attempt ".$this->retries.")");
            sleep(static::$retryDelay);
            return $this->run();
        } else {
            Output::outLine("[OCR] Max retries reached for file: ".$this->pdfFilePath);
            return $result;
        }
    }

    public function run($reference=null) {
        if($reference !== null) {
            $this->reference = $reference;
            
        }
        $this->init();
        $result = null;
        try {
            $result = $this->runExecute();
        } catch (\Exception $e) {
            Output::outLine("[OCR] process failed for file: ".$this->pdfFilePath." with error: ".$e->getMessage());
            return $this->reTryOrReturn("[OCR FAILED - EXCEPTION: ".$e->getMessage()."]");
        }
        if($result) {
            return $result;
        } else {
            Output::outLine("[OCR] process failed for file: ".$this->pdfFilePath);
            return $this->reTryOrReturn("[OCR FAILED - NO RESULT]");
        }
    }

    private function runExecute() {
        $this->init();
        if(!empty($this->message)) {
            if(is_array($this->message)) {
                foreach($this->message as $msg) {
                    $this->chat->addMessage($msg);
                }
            } else {
                $this->chat->addMessage($this->message);
            }
        }
        $this->chat->addFile($this->fileId());
        $result = $this->chat->create(null, function($streamEvent) {
        }, true);
        if($result) {
            return $result->getResult();
        } else {
            Output::outLine("[OCR] No result returned from OCR process for file: ".$this->pdfFilePath);
            return "[OCR FAILED - NO RESULT]";
        }
    }
}