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

class Files extends CI_Controller
{
    public function __construct()
    {
        parent::__construct();
        $this->load->database();
		$this->load->model('CommonModel');
		$this->load->library("pagination");
		$this->load->library("response");
		$this->load->library("ValidateData");
        $this->load->helper(['url', 'security']);
        
    }
    public function fileList()
	{
		$this->access->checkTokenKey();
		$this->response->decodeRequest();
		$textSearch = $this->input->post('textSearch');
		$isAll = $this->input->post('getAll');
		$curPage = $this->input->post('curpage');
		$textval = $this->input->post('textval');
		$orderBy = $this->input->post('orderBy');
		$order = $this->input->post('order');
		$folder_id = $this->input->post('folderID');
		$cmp_type = $this->input->post('cmp_type');
		$record_id = $this->input->post('record_id');

		$config = array();
		if (!isset($orderBy) || empty($orderBy)) {
			$orderBy = "created_date";
			$order = "DESC";
		}
		$other = array("orderBy" => $orderBy, "order" => $order);

		$config = $this->config->item('pagination');
		$wherec = $join = array();
		if (isset($textSearch) && !empty($textSearch) && isset($textval) && !empty($textval)) {
			$textSearch = trim($textSearch);
			$wherec["$textSearch like  "] = "'" . $textval . "%'";
		}
		if (isset($folder_id) && !empty($folder_id)) {
			$wherec["folder_id"] = '= ' . $folder_id . '';
		} else {
			$wherec["folder_id"] = "IS NULL";
		}

		if(isset($cmp_type) && !empty($cmp_type)){
			$wherec["cmp_type = "] = "'".$cmp_type."'";
		}
		if(isset($record_id) && !empty($record_id)){
			$wherec["record_id = "] = "'".$record_id."'";
		}

		$config["base_url"] = base_url() . "mediaFiles";
		$config["total_rows"] = $this->CommonModel->getCountByParameter('media_id', 'media', $wherec);
		$config["uri_segment"] = 2;
		$this->pagination->initialize($config);
		if (isset($curPage) && !empty($curPage)) {
			$curPage = $curPage;
			$page = $curPage * $config["per_page"];
		} else {
			$curPage = 0;
			$page = 0;
		}
		if ($isAll == "Y") {
			$mediaDetails = $this->CommonModel->GetMasterListDetails($selectC = '', 'media', $wherec, '', '', $join, $other);
		} else {
			$mediaDetails = $this->CommonModel->GetMasterListDetails($selectC = '', 'media', $wherec, $config["per_page"], $page, $join, $other);
		}

		//$status['data'] = $mediaDetails;
        $mapped = [];
        foreach ($mediaDetails as $row) {
            $mapped[] = $this->mapMediaRow($row);
        }
        $status['data'] = $mapped;
		$status['paginginfo']["curPage"] = $curPage;
		if ($curPage <= 1)
			$status['paginginfo']["prevPage"] = 0;
		else
			$status['paginginfo']["prevPage"] = $curPage - 1;

		$status['paginginfo']["pageLimit"] = $config["per_page"];
		$status['paginginfo']["nextpage"] =  $curPage + 1;
		$status['paginginfo']["totalRecords"] =  $config["total_rows"];
		$status['paginginfo']["start"] =  $page;
		$status['paginginfo']["end"] =  $page + $config["per_page"];
		$status['loadstate'] = true;
		if ($config["total_rows"] <= $status['paginginfo']["end"]) {
			$status['msg'] = $this->systemmsg->getErrorCode(232);
			$status['statusCode'] = 400;
			$status['flag'] = 'S';
			$status['loadstate'] = false;
			$this->response->output($status, 200);
		}
		if ($mediaDetails) {
			$status['msg'] = "sucess";
			$status['statusCode'] = 400;
			$status['flag'] = 'S';
			$this->response->output($status, 200);
		} else {
			$status['msg'] = $this->systemmsg->getErrorCode(227);
			$status['statusCode'] = 227;
			$status['flag'] = 'F';
			$this->response->output($status, 200);
		}
	}

    /* =========================================================
     * PUBLIC ENDPOINTS
     * ========================================================= */
    private function mapMediaRow($row)
    {
        $token = rtrim(strtr(
            base64_encode($row->media_id . '|' . $this->company_id),
            '+/', '-_'
        ), '=');

        $mime = $row->mime_type ?? 'application/octet-stream';

        $isStreamable = (
            strpos($mime, 'video/') === 0 ||
            strpos($mime, 'audio/') === 0
        );

        return [
            'id'        => (int)$row->media_id,
            'name'      => $row->media_key,
            //'size'      => (int)$row->file_size,
            'mimeType'  => $mime,
            'fileType'  => $row->media_type,
            'createdAt'=> $row->created_date,
            'token'     => $token,

            // frontend-friendly URLs
            'viewUrl'      => $isStreamable ? null : base_url("/files/view/{$token}"),
            'downloadUrl'  => base_url("/files/download/{$token}"),
            'streamUrl'    => $isStreamable ? base_url("/files/stream/{$token}") : null,
        ];
}

    // Inline view: images, pdf
    public function view($token)
    {
        $file = $this->resolveFileFromToken($token);
        //$this->assertPermission($file);
        $this->outputFile($file, 'inline');
    }

    // Force download
    public function download($token)
    {
        $file = $this->resolveFileFromToken($token);
        //$this->assertPermission($file);

        $this->outputFile($file, 'download');
    }

    // Video / audio streaming
    public function stream($token)
    {
        $file = $this->resolveFileFromToken($token);
        $this->assertPermission($file);

        $this->streamFile($file);
    }

	// Video / audio streaming
    public function deleteFile()
	{
		$this->access->checkTokenKey();
		$this->response->decodeRequest();

		$fileId = (int) $this->input->post('file_id');
		$where = array(
				'media_id'  => $fileId,
				'company_id'=> $this->company_id);
		$file = $this->CommonModel->getMasterDetails('media','*',$where);
		if(empty($file)) {
			return $this->respondError('File not found', 404);
		}

		/* -------------------------------------------------
		Resolve tenant + company paths
		------------------------------------------------- */

		$tenantRes = getTenantKey();
		if ($tenantRes['flag'] === 'F') {
			return $this->response->output($tenantRes, 200);
		}

		$companyRes = getCompanyKey($this->company_id);
		if ($companyRes['flag'] === 'F') {
			return $this->response->output($companyRes, 200);
		}

		$mediaPathArr = ensureTenantMediaPath(
			$this->config->item('mediaPATH'),
			$tenantRes['tenant_key'],
			$companyRes['company_key'],
			'files'
		);

		$basePath = rtrim($mediaPathArr['path'], '/') . '/';
		$mediaKey = $file[0]->media_key;

		$originalFile = realpath($basePath . $mediaKey);

		/* -------------------------------------------------
		Safety check
		------------------------------------------------- */
		// print $mediaKey."<br>";
		// print $originalFile."<br>";
		// print $basePath;
		if (!$originalFile || strpos($originalFile, realpath($basePath)) !== 0) {
			$del = $this->CommonModel->deleteMasterDetails("media",$where);
			return $this->response->output([
				'flag' => 'S',
				'msg'  => 'File deleted successfully'
			], 200);
		}

		/* -------------------------------------------------
		Delete original file
		------------------------------------------------- */
		if (is_file($originalFile)) {
			@unlink($originalFile);
			clearstatcache(); // ensure FS cache is updated
		}

		/* -------------------------------------------------
		Delete thumbnails
		example:
		facebook-in(1)_150x150.jpg
		facebook-in(1)_350x240.jpg
		facebook-in(1)_650x460.jpg
		------------------------------------------------- */

		$pathInfo = pathinfo($mediaKey);
		$name     = $pathInfo['filename'];
		$ext      = isset($pathInfo['extension']) ? '.' . $pathInfo['extension'] : '';

		$thumbnailSizes = [
			'150x150',
			'350x240',
			'650x460'
		];

		foreach ($thumbnailSizes as $size) {
			$thumbPath = $basePath . $name . '_' . $size . $ext;
			$thumbReal = realpath($thumbPath);

			if ($thumbReal && strpos($thumbReal, realpath($basePath)) === 0 && is_file($thumbReal)) {
				@unlink($thumbReal);
			}
		}

		/* -------------------------------------------------
		Delete DB record (soft delete recommended)
		------------------------------------------------- */

		// $this->db->where('media_id', $fileId)->update('media', [
		// 	'is_deleted' => 1,
		// 	'deleted_at'=> date('Y-m-d H:i:s'),
		// 	'deleted_by'=> $this->user_id ?? null
		// ]);
		$del = $this->CommonModel->deleteMasterDetails("media",$where);

		return $this->response->output([
			'flag' => 'S',
			'msg'  => 'File and thumbnails deleted successfully'
		], 200);
	}


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

    /**
     * Decode token, load DB record, attach real file path
     */
    private function resolveFileFromToken($token)
    {
        $decoded = base64_decode(strtr($token, '-_', '+/'), true);

        if (!$decoded || strpos($decoded, '|') === false) {
            $this->respondError('Invalid file token', 400);
        }

        [$fileId, $companyId] = explode('|', $decoded);

        if (!$fileId || !$companyId) {
            $this->respondError('Invalid file token', 400);
        }

        // 🔒 Load file record
        //'company_id' => (int)$companyId
        $file = $this->CommonModel->getMasterDetails(
            'media','*',
            ['media_id' =>(int)$fileId]
        );//'status' => 'active'

        if (empty($file)) {
            $this->respondError('File not found', 404);
        }
       //print_r($file);
        // ⚠️ Real file path (NOT exposed)

        $tenantRes = getTenantKey();

		if ($tenantRes['flag'] === 'F') {
			$this->response->output($tenantRes, 200);
			return;
		}

		$tenantKey = $tenantRes['tenant_key'];

		$companyRes = getCompanyKey($companyId);
		if ($companyRes['flag'] === 'F') {
			$this->response->output($companyRes, 200);
			return;
		}
		$companyKey = $companyRes['company_key'];

		$mediaPathArr = ensureTenantMediaPath(
			$this->config->item('mediaPATH'),
			$tenantKey,
			$companyKey,
			'files'
		);
        $path = $mediaPathArr['path']. $file[0]->media_key;

        if (!is_file($path)) {
            $this->respondError('File missing on server', 404);
        }

        // Normalize structure
        return [
            'id'            => $file[0]->media_id,
            'company_id'    => $file[0]->company_id,
            'original_name' => $file[0]->original_name,
            'mime_type'     => $file[0]->mime_type,
            'size'          => (int)$file[0]->file_size,
            'path'          => $path,
        ];
    }

    /**
     * Permission check (multi-tenant safe)
     */
    private function assertPermission(array $file)
    {
        if (!$user) {
            $this->respondError('Unauthorized', 401);
        }

        if ((int)$file['company_id'] !== (int)$user['company_id']) {
            $this->respondError('Forbidden', 403);
        }
    }

    /* =========================================================
     * FILE OUTPUT
     * ========================================================= */

    /**
     * Inline / download output (small & medium files)
     */
    private function outputFile(array $file, string $mode = 'inline')
    {
        //print_r($file); exit;
        $mime = $file['mime_type'] ?? 'application/octet-stream';

        header('Content-Type: ' . $mime);
        header('Content-Length: ' . $file['size']);
        header(
            'Content-Disposition: ' .
            ($mode === 'download' ? 'attachment' : 'inline') .
            '; filename="' . basename($file['original_name']) . '"'
        );
        header('X-Content-Type-Options: nosniff');
        header('Cache-Control: private, max-age=86400');

        readfile($file['path']);
        exit;
    }

    /**
     * Video / audio streaming with HTTP Range support
     */
    private function streamFile(array $file)
    {   
        $path = $file['path'];
        $size = filesize($path);
        $mime = $file['mime_type'] ?: 'application/octet-stream';

        $start = 0;
        $end   = $size - 1;

        header("Content-Type: {$mime}");
        header("Accept-Ranges: bytes");

        if (isset($_SERVER['HTTP_RANGE'])) {
            if (preg_match('/bytes=(\d+)-(\d*)/', $_SERVER['HTTP_RANGE'], $matches)) {
                $start = (int)$matches[1];
                if ($matches[2] !== '') {
                    $end = (int)$matches[2];
                }
                header("HTTP/1.1 206 Partial Content");
            }
        }

        $length = $end - $start + 1;
        header("Content-Length: {$length}");
        header("Content-Range: bytes {$start}-{$end}/{$size}");

        $fp = fopen($path, 'rb');
        fseek($fp, $start);

        $buffer = 8192;
        while (!feof($fp) && ftell($fp) <= $end) {
            if (ftell($fp) + $buffer > $end) {
                $buffer = $end - ftell($fp) + 1;
            }
            echo fread($fp, $buffer);
            flush();
        }

        fclose($fp);
        exit;
    }

    /* =========================================================
     * UTIL
     * ========================================================= */

    private function respondError(string $msg, int $status)
    {
        http_response_code($status);
        header('Content-Type: application/json');
        echo json_encode(['error' => $msg]);
        exit;
    }

    public function uploadFiles($pathTOSave = '')
	{
		$pathTOSave = $pathTOSave ?? '';
		$this->access->checkTokenKey();
		$this->response->decodeRequest();
		$extraData = array();
		// $cmpType = $_GET['cmpType'];
		// print_r($pathTOSave);exit;
		$cmsType = $_GET['cmsType'] ?? null;
		if(isset($cmsType) && !empty($cmsType)){
			$extraData["cmp_type"] = $cmsType;
		}
		$module = $this->input->post('module');
        $record_id = $this->input->post('record_id');
		
		$tenantRes = getTenantKey();

		if ($tenantRes['flag'] === 'F') {
			$this->response->output($tenantRes, 200);
			return;
		}

		$tenantKey = $tenantRes['tenant_key'];

		$companyRes = getCompanyKey($this->company_id);
		if ($companyRes['flag'] === 'F') {
			$this->response->output($companyRes, 200);
			return;
		}
		$companyKey = $companyRes['company_key'];

		$mediaPathArr = ensureTenantMediaPath(
			$this->config->item('mediaPATH'),
			$tenantKey,
			$companyKey,
			'files'
		);
		
		if ($mediaPathArr['flag'] === 'F') {
			$this->response->output($mediaPathArr, 200);
			return;
		}

		$baseMediaPath = $mediaPathArr['path'];

		//$baseMediaPath = $mediaPathArr['path'];
		
		
		if (!empty($pathTOSave) && $pathTOSave != 0) {
			// get folder name from tabel
			$dirname = $this->CommonModel->getMasterDetails("media", "*", array("media_id" => $pathTOSave));
			//print_r($dirname); exit;
			if (empty($dirname)) {
				$status['msg'] = $this->systemmsg->getErrorCode(273);
				$status['statusCode'] = 227;
				$status['flag'] = 'F';
				$this->response->output($status, 200);
			} else {
				$extraData["folder_id"] = $pathTOSave;
				$pathTOSave = $baseMediaPath . "/" . $dirname[0]->media_key;
			}
		} else {
			$pathTOSave = $baseMediaPath;
		}

		$this->load->library('core/realtimeupload');
		if (!is_dir($pathTOSave)) {
			mkdir($pathTOSave, 0777);
			chmod($pathTOSave, 0777);
		} else {
			if (!is_writable($pathTOSave)) {
				chmod($pathTOSave, 0777);
			}
		}
		//print $pathTOSave; exit;
		$settings = array(
			'uploadFolder' => $pathTOSave,
			'extension' => ['webp','png', 'pdf', 'jpg', 'jpeg', 'svg', 'gif', 'mp4', 'avi', 'mkv', 'mp3', 'ogg', 'wav', 'docx', 'doc', 'xls', 'xlsx'],
			'maxFolderFiles' => 0,
			'maxFolderSize' => 0,
			'maxFileSize' => 10485760,
			'returnLocation' => false,
			'rename'=>true,
			'uniqueFilename' => false,
			'dbTable' => 'media',
			'fileTypeColumn' => 'media_type',
			'fileColumn' => 'media_key',
			'forignKey' => '',
			'forignValue' => '',
			'docType' => "",
            "company_id"=>$this->company_id,
			'docTypeValue' => '',
			'isSaveToDB' => "Y",
			'module'=>$module,
        	'record_id'=>$record_id,
			'extraData' => $extraData,
		);
        //$uploader = new RealTimeUpload();
		$this->realtimeupload->init($settings);
	}
}
