<?php
namespace boru\boruai\Models;

use boru\boruai\BoruAI;
use boru\query\Entity;
use boru\query\models\EntityDefinition;
use boru\query\Query;
use boru\boruai\Openai\OpenAIFile as OpenAIFile;
use boru\boruai\Openai\OpenAIUpload;
use boru\boruai\Tools\OCR;
use boru\query\Factory;

class File extends Entity {
    /** @var OpenAIFile */
    private $openAIFile;


    public static function entityDefinition() {
        $tableName = BoruAI::table('files');
        return new EntityDefinition([
            "className" => static::class,
            "name" => "File",
            "queryBase" => Query::create()->select('*')->from($tableName),
            "createdField" => 'created_at',
        ]);
    }
    public function __construct($idOrData=null) {
        parent::__construct($idOrData);
        $this->set('ocr_tokens',"0");
    }

    //actions

    public function deleteLocal() {
        if($this->path() && file_exists($this->path())) {
            unlink($this->path());
        }
    }

    public function delete() {
        
        $this->deleteLocal();
        if($this->file()) {
            $this->openAIFile->delete();
        }
        Query::create()->delete(BoruAI::table('files'))->where('id',"=",$this->id())->run();
    }

    public function upload($delete=false) {
        $upload = new OpenAIUpload();
        $upload->purpose($this->purpose());
        $upload->upload($this->path());
        $uploadfile = $upload->file()->toArray();
        if($uploadfile["id"] && $delete) {
            unlink($this->path());            
        }
        $uploadFile["fileid"] = $uploadfile["id"];
        unset($uploadfile["id"]);
        $this->file($upload->file());
        $this->fileid($upload->file()->id());
        $this->save();
    }

    public function queueUpload() {
        Queue::queue($this,"upload");
    }

    public function queueOcr() {
        Queue::queue($this,"ocr");
    }

    public function setOcr($content,$tokens=0) {
        $this->ocrContent($content);
        $this->ocrTokens($tokens);
        $this->ocrDate(date('Y-m-d H:i:s'));
        $this->save();
        if($this->docid()) {
            $doc = Factory::instanceById("Document",$this->docid());
            $doc->updateOcr();
        }
    }

    //getter and setter methods
    public function id($id=null) {
        if ($id !== null) {
            $this->set('id', $id);
            return $this;
        }
        return $this->get('id');
    }

    public function path($path=null) {
        if ($path !== null) {
            $this->set('path', $path);
            return $this;
        }
        return $this->get('path');
    }

    public function fileid($fileid=null) {
        if ($fileid !== null) {
            $this->set('fileid', $fileid);
            return $this;
        }
        return $this->get('fileid');
    }

    public function reference($reference=null) {
        if ($reference !== null) {
            $this->set('reference', $reference);
            return $this;
        }
        return $this->get('reference');
    }

    public function file($file=null) {
        if ($file !== null) {
            $this->openAIFile = $file;
            return $this;
        }
        if($this->openAIFile === null && $this->fileid()) {
            $this->openAIFile = OpenAIFile::fromId($this->fileid());
        }
        return $this->openAIFile;
    }

    public function ocr($queue=true,$options=[]) {
        if(!$queue) {
            $ocr = new OCR($this,$options);
            $result = $ocr->run();
            if($result) {
                $this->setOcr($result,$ocr->tokens());
            }
        } else {
            $this->queueOcr();
        }
    }

    public function docid($docid=null) {
        if ($docid !== null) {
            $this->set('docid', $docid);
            return $this;
        }
        return $this->get('docid');
    }

    public function object($object=null) {
        if ($object !== null) {
            $this->set('object', $object);
            return $this;
        }
        return $this->get('object');
    }

    public function created_at($created_at=null) {
        if ($created_at !== null) {
            $this->set('created_at', $created_at);
            return $this;
        }
        return $this->get('created_at');
    }

    public function filename($filename=null) {
        if ($filename !== null) {
            $this->set('filename', $filename);
            return $this;
        }
        return $this->get('filename');
    }

    public function purpose($purpose=null) {
        if ($purpose !== null) {
            $this->set('purpose', $purpose);
            return $this;
        }
        return $this->get('purpose');
    }

    public function bytes($bytes=null) {
        if ($bytes !== null) {
            $this->set('bytes', $bytes);
            return $this;
        }
        return $this->get('bytes');
    }

    public function md5($md5=null) {
        if ($md5 !== null) {
            $this->set('md5', $md5);
            return $this;
        }
        return $this->get('md5');
    }
    public function summary($summary=null) {
        return $this->ocrContent($summary);
    }
    public function ocrContent($ocr_content=null) {
        if ($ocr_content !== null) {
            $this->set('ocr_content', $ocr_content);
            return $this;
        }
        return $this->get('ocr_content');
    }

    public function ocrDate($ocr_date=null) {
        if ($ocr_date !== null) {
            $this->set('ocr_date', $ocr_date);
            return $this;
        }
        return $this->get('ocr_date');
    }

    public function ocrTokens($ocr_tokens=null) {
        if ($ocr_tokens !== null) {
            $this->set('ocr_tokens', $ocr_tokens);
            return $this;
        }
        return $this->get('ocr_tokens');
    }

    public function asJpg($base64=false) {
        $fileId = $this->id();
        $content = null;
        if(file_exists($this->path())) {
            $content = file_get_contents($this->path());
        } else {
            throw new \Exception("File not found");
        }
        $maxFileSizeMegs = BoruAI::config("ocrspace.max_file_size_megs", 1);
        $maxFileSize = $maxFileSizeMegs * 1024 * 1024;
        $outFile = $this->path().$fileId.'.jpg';
        $fileImage = new \Imagick();
        $fileImage->setResolution(300,300);
        $fileImage->readimage($this->path());
        $fileImage->setImageFormat('JPEG');
        $fileImage->setImageAlphaChannel(\imagick::ALPHACHANNEL_ACTIVATE);
        $fileImage->setBackgroundColor(new \ImagickPixel('#ffffff'));
        $fileImage->writeImage($outFile);
        $quality = 95;
        while(strlen($fileImage->getImageBlob()) > $maxFileSize) {
            $quality -= 5;
            $fileImage->setImageCompressionQuality($quality);
            if($quality < 5) {
                throw new \Exception("File size too large");
            }
            $fileImage->writeImage($outFile);
        }
        $imageContents = file_get_contents($outFile);
        unlink($outFile);

        if($base64) {
           return base64_encode($imageContents);
        }
        return $fileImage;
    }

    //static methods

    public static function fromFile($file) {
        $file = new static($file->toArray());
        $file->file($file);
        return $file;
    }

    /**
     * Get a file by id
     * @param string $id
     * @return File|false
     */
    public static function fromId($id) {
        $files = Factory::search("File",[0,1],["id","=",$id]);
        if($files) {
            return new self($files[0]->id());
        }
        return false;
    }

    public static function create($path,$docId=null,$purpose=null) {
        $file = new static();
        $file->path($path);
        $file->filename(basename($path));
        if($docId) {
            $file->docid($docId);
            if(!$purpose) {
                $doc = new Document($docId);
                $purpose = $doc->purpose();
            }
        }
        $file->purpose($purpose);
        $file->save();
        return $file;
    }

    public static function uploadFile($filename,$purpose="assistants",$docId=null,$queue=false) {
        $file = new static();
        $file->path($filename);
        $file->filename(static::cleanFilename(basename($filename)));
        $file->purpose($purpose);
        if($docId) {
            $file->docid($docId);
        }
        $file->save();
        if($queue) {
            Queue::queue($file,"upload");
        } else {
            $file->upload();
        }
        $file = new static($file->id());
        return $file;
    }

    public static function fromFileId($fileId) {
        if(empty($fileId)) {
            return false;
        }
        $files = Factory::search("File",[0,1],["fileid","=",$fileId]);
        if($files) {
            return new self($files[0]->id());
        }
        return false;
    }

    /**
     * Search for files by reference
     * @param mixed $reference 
     * @param array $limit [offset, count]
     * @return File[]|false 
     */
    public static function searchByReference($reference, $limit=[0,10]) {
        $files = Factory::search("File",$limit,["reference","=",$reference]);
        if($files) {
            $newFiles = [];
            foreach($files as $file) {
                $newFiles[] = new self($file->id());
            }
            return $newFiles;
        }
        return false;
    }

    /**
     * Get a file by path
     * @param string $path
     * @return File|false
     */
    public static function fileFromPath($path) {
        $file = Factory::search("File",[0,1],["path","=",$path]);
        if($file) {
            return new self($file[0]->id());
        }
        return false;
    }

    
    public static function cleanFilename($filename) {
        // Remove spaces and convert to lowercase
        $filename = str_replace(' ', '_', $filename);
        $filename = strtolower($filename);
        // Remove any special characters except for underscores and hyphens
        $filename = preg_replace('/[^a-z0-9_\-\.]/', '', $filename);
        return $filename;
    }
}