<?php

namespace App\Helpers;

use Examyou\RestAPI\Exceptions\ApiException;

class Files
{
    /**
     * Validate uploaded file to prevent malicious uploads
     *
     * @param mixed $uploadedFile
     * @throws ApiException
     * @return void
     */
    public static function validateUploadedFile($uploadedFile)
    {
        // Check if file is valid
        if (!$uploadedFile->isValid()) {
            throw new ApiException('File was not uploaded correctly');
        }

        // Disallow dangerous extensions
        $forbiddenExtensions = [
            'php',
            'php3',
            'php4',
            'php5',
            'phtml',
            'phar',
            'sh',
            'htaccess',
            'pl',
            'cgi',
            'exe',
            'bat',
            'cmd',
            'com',
            'scr',
            'dll',
            'js',
            'jsp',
            'asp',
            'aspx',
            'cer',
            'csr',
            'jspx',
            'war',
            'jar',
            'vb',
            'vbs',
            'wsf',
            'ps1',
            'ps2',
            'xml'
        ];

        // Disallow dangerous MIME types
        $forbiddenMimeTypes = [
            'text/x-php',
            'application/x-php',
            'application/x-sh',
            'text/x-shellscript',
            'application/x-msdownload',
            'application/x-msdos-program',
            'application/x-executable',
            'application/x-csh',
            'application/x-bat',
            'application/x-msdos-windows',
            'application/x-javascript',
            'text/javascript',
            'application/javascript',
            'application/x-ms-installer',
            'application/x-dosexec',
            'application/x-cgi',
            'application/x-perl',
            'text/x-perl',
            'application/x-python',
            'text/x-python',
            'application/x-msdos-batch',
            'application/x-msdos-cmd',
            'application/x-msdos-com',
            'application/x-msdos-scr',
            'application/x-msdos-dll',
            'application/x-msdos-js',
            'application/x-msdos-vbs',
            'application/x-msdos-ps1',
            'application/xml',
            'text/xml'
        ];

        $extension = strtolower($uploadedFile->getClientOriginalExtension());
        $mimeType = strtolower($uploadedFile->getMimeType());
        $originalName = strtolower($uploadedFile->getClientOriginalName());

        // Prevent double extensions (e.g. file.php.jpg)
        if (preg_match('/\.(php[0-9]?|phtml|phar|sh|pl|cgi|exe|bat|cmd|com|scr|dll|js|jsp|asp|aspx|cer|csr|jspx|war|jar|vb|vbs|wsf|ps1|ps2|xml)(\..+)?$/i', $originalName)) {
            throw new ApiException('You are not allowed to upload files with dangerous extensions');
        }

        if (in_array($extension, $forbiddenExtensions)) {
            throw new ApiException('You are not allowed to upload files with extension: ' . $extension);
        }

        if (in_array($mimeType, $forbiddenMimeTypes)) {
            throw new ApiException('You are not allowed to upload files with mime type: ' . $mimeType);
        }

        // Prevent uploading .htaccess or similar files by name
        if (strpos($originalName, '.htaccess') !== false) {
            throw new ApiException('You are not allowed to upload .htaccess files');
        }

        // Prevent uploading files with size less than 10 bytes
        if ($uploadedFile->getSize() <= 10) {
            throw new ApiException('You are not allowed to upload a file with filesize less than 10 bytes');
        }

        // Prevent uploading files with null or empty extension
        if (empty($extension)) {
            throw new ApiException('File must have a valid extension');
        }

        // Optionally, limit file name length
        if (strlen($uploadedFile->getClientOriginalName()) > 255) {
            throw new ApiException('File name is too long');
        }
    }

    /**
     * Generate a new unique file name
     *
     * @param string $currentFileName
     * @return string
     */
    public static function generateNewFileName($currentFileName)
    {
        $ext = strtolower(pathinfo($currentFileName, PATHINFO_EXTENSION));
        $newName = md5(microtime() . uniqid());

        return ($ext === '') ? $newName : $newName . '.' . $ext;
    }

    /**
     * Get allowed image extensions
     *
     * @return array
     */
    public static function getAllowedImageExtensions()
    {
        return ['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp', 'bmp'];
    }

    /**
     * Get allowed document extensions
     *
     * @return array
     */
    public static function getAllowedDocumentExtensions()
    {
        return ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'csv'];
    }

    /**
     * Get allowed font extensions
     *
     * @return array
     */
    public static function getAllowedFontExtensions()
    {
        return ['ttf', 'otf', 'woff', 'woff2', 'eot'];
    }

    /**
     * Validate if file extension is allowed for the given type
     *
     * @param mixed $uploadedFile
     * @param string $type (image|document|font)
     * @throws ApiException
     * @return void
     */
    public static function validateFileType($uploadedFile, $type = 'image')
    {
        $extension = strtolower($uploadedFile->getClientOriginalExtension());
        $allowedExtensions = [];

        switch ($type) {
            case 'image':
                $allowedExtensions = self::getAllowedImageExtensions();
                break;
            case 'document':
                $allowedExtensions = self::getAllowedDocumentExtensions();
                break;
            case 'font':
                $allowedExtensions = self::getAllowedFontExtensions();
                break;
        }

        if (!in_array($extension, $allowedExtensions)) {
            throw new ApiException('File type not allowed. Allowed types: ' . implode(', ', $allowedExtensions));
        }
    }
}
