<?php
defined('BASEPATH') OR exit('No direct script access allowed');

/**
 * RealtimeUploadR2
 * ----------------
 * R2-compatible replacement for realtimeupload
 */
class RealtimeUploadR2
{
    protected $CI;
    //protected $settings = [];
    protected $r2;
    protected $settings = array(
		'uploadFolder' =>  'uploads', // name of the upload folder
		'maxFileSize' => 2097152, // max file size in KB
		'extension' => [''], // whitelist. If no extension is specified, nothing can be uploaded
		'rename' => true, // required for security
		'overwrite' => false, // if set to false, file with identical name will be renamed
		'maxFolderFiles' => 0, // number of file that can be uploaded in the destination folder (NOT file per user)
		'maxFolderSize' => 0, // size in KB, 0 means no limit
		'protectUploadDirectory' => true, // disallow listing or execution within the upload directory
		'denyUploadDirectoryAccess' => false, // disallow access to the upload folder
		'uniqueFilename' => false, // if set to true, generate a random filename for each uploaded file
		'returnLocation' => false, // return the url of the uploaded file
		'maxWidth' => 0, // maximum width allowed for an image
		'maxHeight' => 0, // maximum height allowed for an image
		'forignValue' => 0, // memberID to update databse records
		'fileType' => 'image',
		'dbTable' => null,
		'fileTypeColumn' => null,
		'fileColumn' => null,
		'forignKey' => null,
        'source'=>'local',
		'extraData' => null,
		'isSaveToDB' => "Y",
		'isUpdate' => "N",
		'primaryKey' => null,
		'primaryValue' => null,
        'temp_reference_id'=> null,
        'module'=>null,
        'company_id'=>null,
        'record_id'=>null,

	);
    public function __construct()
    {
        $this->CI = &get_instance();
        $this->CI->load->library('core/R2Storage');
        $this->r2 = $this->CI->r2storage;
    }

    /* =========================================================
     * ENTRY POINT
     * ========================================================= */
    public function init(array $settings)
    {
        //$this->settings = $settings;
        foreach ($this->settings as $key => $value) {
			if (array_key_exists($key, $settings)) {
				$this->settings[$key] = $settings[$key];
			}
		}
        //log_message('debug', 'R2 process fail'.$this->settings['source']);
        if (empty($_FILES)) {
            $this->error('No file received');
        }
        $results = [];
        foreach ($_FILES as $file) {

            // Handle multi-file (Uppy / array)
            if (is_array($file['name'])) {
                foreach ($file['name'] as $i => $name) {
                   $results[] =  $this->processFile([
                        'name'     => $file['name'][$i],
                        'type'     => $file['type'][$i] ?? null,
                        'tmp_name' => $file['tmp_name'][$i],
                        'error'    => $file['error'][$i],
                        'size'     => $file['size'][$i],
                    ]);
                }
            }
            // Single file
            else {
                $results[] = $this->processFile($file);
            }
        }
        if (($this->settings['source'] ?? '') == 'whatsapp'){
            return $results;
        }else{
            $this->success($results);    
        }
        
    }

    /* =========================================================
     * PROCESS FILE
     * ========================================================= */
    protected function processFile(array $file)
    {
        //log_message('debug', 'R2 process file  start');
        // 1️⃣ Upload status
        if ($file['error'] !== UPLOAD_ERR_OK) {
            $this->error('Upload failed');
        }

        // 2️⃣ Security check
        // if (!is_uploaded_file($file['tmp_name'])) {
        //     $this->error('Invalid upload source');
        // }

        if (
            ($this->settings['source'] ?? '') !== 'whatsapp' &&
            !is_uploaded_file($file['tmp_name'])
        ) {
            $this->error('Invalid upload source');
        }

        // 3️⃣ Original filename (can be "blob")
        $originalName = $file['name'] ?? 'blob';

        // 4️⃣ Extension detection
        $ext = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));

        if ($ext === '') {
            $ext = $this->detectExtensionFromFile($file['tmp_name']);
        }

        if (!$ext) {
            $this->error('Unsupported or unknown file type');
        }

        // 5️⃣ Validate extension
        $allowedExt = array_map('strtolower', $this->settings['extension'] ?? []);

        if (!in_array($ext, $allowedExt, true)) {
            $this->error('Invalid file type: '.$ext);
        }

        // 6️⃣ File size check
        if (!empty($this->settings['maxFileSize']) &&
            $file['size'] > $this->settings['maxFileSize']) {
            $this->error('File size exceeded');
        }

        // 7️⃣ Detect MIME (DO NOT TRUST $file['type'])
        $detectedMime = $this->detectMimeFromFile($file['tmp_name']);

        // 8️⃣ Determine media_type
        $mediaType = 'file';
        if (str_starts_with($detectedMime, 'image/')) {
            $mediaType = 'image';
        } elseif (str_starts_with($detectedMime, 'video/')) {
            $mediaType = 'video';
        } elseif (str_starts_with($detectedMime, 'audio/')) {
            $mediaType = 'audio';
        }

        // 9️⃣ Final filename
       $finalName = $this->settings['rename']
        ? uniqid('file_', true) . '.' . $ext
        : time() . '_' . preg_replace('/[^a-zA-Z0-9._-]/', '_', $originalName);

        // 🔟 Build R2 key
        $r2Key = $this->buildR2Key($finalName);
        //log_message('debug', 'R2 upload start');
        // 1️⃣1️⃣ Upload to R2
        $this->r2->upload(
            $r2Key,
            $file['tmp_name'],
            $detectedMime
        );

        // 1️⃣2️⃣ Save DB
        if (($this->settings['isSaveToDB'] ?? 'N') === 'Y') {
            $last_id = $this->saveToDB(
                $finalName,
                $originalName,
                $file,
                $detectedMime,
                $mediaType
            );
        }

        // 1️⃣3️⃣ Cleanup temp
        @unlink($file['tmp_name']);
        //log_message('error', 'thumb detect creating: '.$mediaType);
        if (($mediaType === 'image' || $mediaType === 'video') && $ext !== 'svg') {

            // $cmd = sprintf(
            //     'php %s index.php core/jobs/ThumbnailJob generate %s %s > /dev/null 2>&1 &',
            //     FCPATH,
            //     escapeshellarg($mediaType),
            //     escapeshellarg($r2Key)
            // );
            $cmd = sprintf(
                'cd %s && php index.php core/jobs/ThumbnailJob generate %s %s > /dev/null 2>&1 &',
                escapeshellarg(FCPATH),
                escapeshellarg($mediaType),
                escapeshellarg($r2Key)
            );
            //log_message('error', 'thumb creating: '.$cmd);

            if (function_exists('exec')) {
                exec($cmd);
            } else {
                log_message('error', 'exec disabled, thumbnail skipped: '.$r2Key);
            }
        }
        return [
            'file' => $finalName,
            'media_id' =>$last_id,
            'access_key' => rtrim(
                strtr(base64_encode($last_id.'|'.$this->settings['company_id']), '+/', '-_'),
                '='
            ),
            'mime' => $detectedMime,
            'media_type' => $mediaType
        ];
    }

    /* =========================================================
     * HELPERS
     * ========================================================= */

    protected function buildR2Key(string $fileName): string
    {
        $tenant  = getTenantKey();
        $company = getCompanyKey($this->settings['company_id']);

        return
            "tenant_{$tenant['tenant_key']}/" .
            "company_{$company['company_key']}/files/" .
            $fileName;
    }

    protected function detectMimeFromFile(string $tmpPath): string
    {
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $mime  = finfo_file($finfo, $tmpPath);
        finfo_close($finfo);

        return $mime ?: 'application/octet-stream';
    }

    protected function detectExtensionFromFile(string $tmpPath): ?string
    {
        $mime = $this->detectMimeFromFile($tmpPath);

        $map = [
            'image/png'  => 'png',
            'image/jpeg' => 'jpg',
            'image/webp' => 'webp',
            'image/gif'  => 'gif',
            'image/svg+xml' => 'svg',

            'application/pdf' => 'pdf',
            'application/msword' => 'doc',
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
            'application/vnd.ms-excel' => 'xls',
            'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',

            'audio/mpeg' => 'mp3',
            'audio/wav'  => 'wav',
            'audio/ogg'  => 'ogg',

            'video/mp4'  => 'mp4',
            'video/x-msvideo' => 'avi',
            'video/x-matroska' => 'mkv',
        ];

        return $map[$mime] ?? null;
    }

    protected function saveToDB(
        string $finalName,
        string $originalName,
        array $file,
        string $mime,
        string $mediaType
    ) {
        $data = [
            'media_key'     => $finalName,
            'original_name' => $originalName,
            'media_type'    => $mediaType,
            'mime_type'     => $mime,
            'file_size'     => $file['size'],
            'company_id'    => $this->settings['company_id'],
            'created_date'  => date('Y-m-d H:i:s'),
        ];

        if (!empty($this->settings['extraData'])) {
            $data = array_merge($data, $this->settings['extraData']);
        }
        if (!empty($this->settings['module'])) {
            $data['module'] = $this->settings['module'];
        }
        if (!empty($this->settings['record_id'])) {
            $data['record_id'] = $this->settings['record_id'];
        }
        if (!empty($this->settings['temp_reference_id'])) {
            $data['temp_reference_id'] = $this->settings['temp_reference_id'];
        }
        

        $this->CI->db->insert($this->settings['dbTable'], $data);
        return $this->CI->db->insert_id();
    }

    /* =========================================================
     * RESPONSES
     * ========================================================= */

    protected function success(array $payload)
    {
        $this->CI->response->output([
            'flag' => 'S',
            'data' => $payload
        ], 200);
        exit;
    }

    protected function error(string $msg)
    {
        log_message('debug', 'R2 process fail'.$msg);
        $this->CI->response->output([
            'flag' => 'F',
            'msg'  => $msg
        ], 400);
        exit;
    }
}
