<?php

class FileUpload
{
    private const ALLOWED_EXTENSIONS = [
        'jpg', 'jpeg', 'png', 'gif', 'webp',
        'pdf', 'doc', 'docx', 'txt', 'xls', 'xlsx'
    ];

    private const DANGEROUS_EXTENSIONS = [
        'php', 'php3', 'php4', 'php5', 'phtml', 'exe', 'bat', 'cmd', 'sh', 'js', 'jsp', 'asp', 'aspx'
    ];

    public static function upload(array $file, string $destination, array $allowedTypes = []): array
    {
        // Basic upload error check
        if ($file['error'] !== UPLOAD_ERR_OK) {
            return ['success' => false, 'error' => 'Upload failed with error code: ' . $file['error']];
        }

        // File size check
        if ($file['size'] > MAX_UPLOAD_SIZE) {
            return ['success' => false, 'error' => 'File size exceeds maximum allowed size.'];
        }

        // Check if file was actually uploaded
        if (!is_uploaded_file($file['tmp_name'])) {
            return ['success' => false, 'error' => 'Invalid file upload.'];
        }

        // Get file extension and validate
        $extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
        
        // Check for dangerous extensions
        if (in_array($extension, self::DANGEROUS_EXTENSIONS)) {
            return ['success' => false, 'error' => 'File type not allowed.'];
        }

        // Check allowed extensions
        if (!in_array($extension, self::ALLOWED_EXTENSIONS)) {
            return ['success' => false, 'error' => 'File extension not allowed: ' . $extension];
        }

        // MIME type validation
        $mimeType = mime_content_type($file['tmp_name']);
        
        // Additional MIME type validation for images
        if (in_array($extension, ['jpg', 'jpeg', 'png', 'gif', 'webp'])) {
            $allowedMimeTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
            if (!in_array($mimeType, $allowedMimeTypes)) {
                return ['success' => false, 'error' => 'Invalid image file type.'];
            }
            
            // Additional image validation
            if (!self::isValidImage($file['tmp_name'], $extension)) {
                return ['success' => false, 'error' => 'Invalid or corrupted image file.'];
            }
        }

        // Check against allowed types if specified
        if (!empty($allowedTypes) && !in_array($mimeType, $allowedTypes)) {
            return ['success' => false, 'error' => 'File type not allowed: ' . $mimeType];
        }

        // Create destination directory if it doesn't exist
        if (!is_dir($destination)) {
            mkdir($destination, 0755, true);
        }

        // Generate secure random filename
        $filename = self::generateSecureFilename($extension);
        $filepath = $destination . '/' . $filename;

        // Move uploaded file
        if (!move_uploaded_file($file['tmp_name'], $filepath)) {
            return ['success' => false, 'error' => 'Failed to move uploaded file.'];
        }

        // Set secure file permissions
        chmod($filepath, 0644);

        if (str_starts_with($mimeType, 'image/')) {
            self::optimizeUploadedImage($filepath);
        }

        return [
            'success'   => true,
            'filename'  => $filename,
            'filepath'  => $filepath,
            'mime_type' => $mimeType,
            'size'      => $file['size'],
            'original'  => $file['name'],
        ];
    }

    private static function generateSecureFilename(string $extension): string
    {
        // Generate cryptographically secure random filename
        return bin2hex(random_bytes(16)) . '.' . $extension;
    }

    private static function optimizeUploadedImage(string $filepath): void
    {
        if (!function_exists('imagecreatetruecolor') || !file_exists($filepath)) {
            return;
        }

        $imageInfo = @getimagesize($filepath);
        if ($imageInfo === false) {
            return;
        }

        [$originalWidth, $originalHeight, $imageType] = $imageInfo;
        if ($originalWidth <= 0 || $originalHeight <= 0) {
            return;
        }

        $maxDimension = defined('MAX_IMAGE_DIMENSION') ? MAX_IMAGE_DIMENSION : 2000;
        $scale = min(1, $maxDimension / max($originalWidth, $originalHeight));
        $targetWidth = max(1, (int)round($originalWidth * $scale));
        $targetHeight = max(1, (int)round($originalHeight * $scale));

        switch ($imageType) {
            case IMAGETYPE_JPEG:
                $sourceImage = @imagecreatefromjpeg($filepath);
                break;
            case IMAGETYPE_PNG:
                $sourceImage = @imagecreatefrompng($filepath);
                break;
            case IMAGETYPE_WEBP:
                $sourceImage = @imagecreatefromwebp($filepath);
                break;
            default:
                return;
        }

        if ($sourceImage === false) {
            return;
        }

        $targetImage = imagecreatetruecolor($targetWidth, $targetHeight);
        if ($targetImage === false) {
            imagedestroy($sourceImage);
            return;
        }

        if ($imageType === IMAGETYPE_PNG || $imageType === IMAGETYPE_WEBP) {
            imagealphablending($targetImage, false);
            imagesavealpha($targetImage, true);
            $transparent = imagecolorallocatealpha($targetImage, 255, 255, 255, 127);
            imagefilledrectangle($targetImage, 0, 0, $targetWidth, $targetHeight, $transparent);
        }

        $resized = imagecopyresampled(
            $targetImage,
            $sourceImage,
            0,
            0,
            0,
            0,
            $targetWidth,
            $targetHeight,
            $originalWidth,
            $originalHeight
        );

        if (!$resized) {
            imagedestroy($sourceImage);
            imagedestroy($targetImage);
            return;
        }

        $quality = defined('IMAGE_JPEG_QUALITY') ? IMAGE_JPEG_QUALITY : 82;
        switch ($imageType) {
            case IMAGETYPE_JPEG:
                imageinterlace($targetImage, true);
                @imagejpeg($targetImage, $filepath, $quality);
                break;
            case IMAGETYPE_PNG:
                @imagepng($targetImage, $filepath, 6);
                break;
            case IMAGETYPE_WEBP:
                @imagewebp($targetImage, $filepath, $quality);
                break;
        }

        imagedestroy($sourceImage);
        imagedestroy($targetImage);
        chmod($filepath, 0644);
    }

    private static function isValidImage(string $filepath, string $extension): bool
    {
        // Check file size (should not be empty)
        if (filesize($filepath) === 0) {
            return false;
        }

        // Use getimagesize to validate image content
        $imageInfo = @getimagesize($filepath);
        if ($imageInfo === false) {
            return false;
        }

        // Additional validation based on file type
        switch ($extension) {
            case 'jpg':
            case 'jpeg':
                return @imagecreatefromjpeg($filepath) !== false;
            case 'png':
                return @imagecreatefrompng($filepath) !== false;
            case 'gif':
                return @imagecreatefromgif($filepath) !== false;
            case 'webp':
                return @imagecreatefromwebp($filepath) !== false;
            default:
                return true;
        }
    }

    public static function delete(string $filepath): bool
    {
        if (file_exists($filepath) && is_file($filepath)) {
            return unlink($filepath);
        }
        return false;
    }

    public static function getRelativePath(string $absolutePath): string
    {
        return str_replace(APP_ROOT . '/public', '', $absolutePath);
    }

    public static function sanitizeFilename(string $filename): string
    {
        // Remove any path components and sanitize filename
        $filename = basename($filename);
        $filename = preg_replace('/[^a-zA-Z0-9._-]/', '', $filename);
        return $filename;
    }

    public static function createThumbnail(string $sourcePath, string $destinationPath, int $width, int $height, int $quality = 70): array
    {
        if (!file_exists($sourcePath)) {
            return ['success' => false, 'error' => 'Source file does not exist.'];
        }

        // Get image info
        $imageInfo = getimagesize($sourcePath);
        if ($imageInfo === false) {
            return ['success' => false, 'error' => 'Invalid image file.'];
        }

        list($originalWidth, $originalHeight, $imageType) = $imageInfo;

        // Create destination directory if it doesn't exist
        $destinationDir = dirname($destinationPath);
        if (!is_dir($destinationDir)) {
            mkdir($destinationDir, 0755, true);
        }

        // Calculate aspect ratio and new dimensions
        $aspectRatio = $originalWidth / $originalHeight;
        $targetAspectRatio = $width / $height;

        // For square thumbnails (coins), resize to fit in circle without cropping
        // For rectangular thumbnails (banknotes), fit within dimensions
        if ($width == $height) {
            // Square thumbnail - resize to fit, no cropping
            if ($aspectRatio > 1) {
                // Image is wider - fit to width
                $dstWidth = $width;
                $dstHeight = (int)round($width / $aspectRatio);
                $srcX = 0;
                $srcY = 0;
                $newWidth = $originalWidth;
                $newHeight = $originalHeight;
            } else {
                // Image is taller - fit to height
                $dstHeight = $height;
                $dstWidth = (int)round($height * $aspectRatio);
                $srcX = 0;
                $srcY = 0;
                $newWidth = $originalWidth;
                $newHeight = $originalHeight;
            }
        } else {
            // Rectangular thumbnail - only set height, maintain aspect ratio
            $dstHeight = $height;
            $dstWidth = (int)round($height * $aspectRatio);
            $srcX = 0;
            $srcY = 0;
            $newWidth = $originalWidth;
            $newHeight = $originalHeight;
            
            // Update canvas size to match actual thumbnail dimensions
            $width = $dstWidth;
        }

        // Create image resource based on type
        $sourceImage = null;
        switch ($imageType) {
            case IMAGETYPE_JPEG:
                $sourceImage = imagecreatefromjpeg($sourcePath);
                break;
            case IMAGETYPE_PNG:
                $sourceImage = imagecreatefrompng($sourcePath);
                break;
            case IMAGETYPE_GIF:
                $sourceImage = imagecreatefromgif($sourcePath);
                break;
            case IMAGETYPE_WEBP:
                $sourceImage = imagecreatefromwebp($sourcePath);
                break;
            default:
                return ['success' => false, 'error' => 'Unsupported image type.'];
        }

        if ($sourceImage === false) {
            return ['success' => false, 'error' => 'Failed to create image resource.'];
        }

        // Create canvas for thumbnail
        $thumbnail = imagecreatetruecolor($width, $height);
        if ($thumbnail === false) {
            imagedestroy($sourceImage);
            return ['success' => false, 'error' => 'Failed to create thumbnail canvas.'];
        }

        // Handle transparency for PNG and GIF
        if ($imageType === IMAGETYPE_PNG || $imageType === IMAGETYPE_GIF) {
            imagealphablending($thumbnail, false);
            imagesavealpha($thumbnail, true);
            $transparent = imagecolorallocatealpha($thumbnail, 255, 255, 255, 127);
            imagefilledrectangle($thumbnail, 0, 0, $width, $height, $transparent);
        }

        // Resize and crop the image
        $resized = imagecopyresampled(
            $thumbnail, $sourceImage,
            0, 0, $srcX, $srcY,
            $dstWidth, $dstHeight, $newWidth, $newHeight
        );

        if ($resized === false) {
            imagedestroy($sourceImage);
            imagedestroy($thumbnail);
            return ['success' => false, 'error' => 'Failed to resize image.'];
        }

        // Save the thumbnail
        $success = false;
        switch ($imageType) {
            case IMAGETYPE_JPEG:
                $success = imagejpeg($thumbnail, $destinationPath, $quality);
                break;
            case IMAGETYPE_PNG:
                $success = imagepng($thumbnail, $destinationPath, (int)(9 * (1 - $quality / 100)));
                break;
            case IMAGETYPE_GIF:
                $success = imagegif($thumbnail, $destinationPath);
                break;
            case IMAGETYPE_WEBP:
                $success = imagewebp($thumbnail, $destinationPath, $quality);
                break;
        }

        // Clean up
        imagedestroy($sourceImage);
        imagedestroy($thumbnail);

        if ($success) {
            chmod($destinationPath, 0644);
            return ['success' => true, 'filepath' => $destinationPath];
        } else {
            return ['success' => false, 'error' => 'Failed to save thumbnail.'];
        }
    }

    public static function getThumbnailPath(string $originalPath, string $thumbnailDir): string
    {
        $filename = basename($originalPath);
        $extension = pathinfo($filename, PATHINFO_EXTENSION);
        $nameWithoutExt = pathinfo($filename, PATHINFO_FILENAME);
        return $thumbnailDir . '/' . $nameWithoutExt . '_thumb.' . $extension;
    }
}
