<?php

namespace App\Shared\Service;

use App\Shared\Constant\ErrorCodes;
use App\Shared\Exception\AppException;
use Symfony\Component\HttpFoundation\File\UploadedFile;

class FileValidatorService
{
    private const ALLOWED_SIGNATURES = [
        'image/jpeg' => ["\xFF\xD8\xFF"],
        'image/png'  => ["\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"],
        'image/webp' => ["RIFF"],
    ];

    private const ALLOWED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'webp'];

    public function __construct(private readonly int $maxSizeBytes = 5_242_880) {}

    public function validateUploadedFile(UploadedFile $file): string
    {
        if ($file->getSize() > $this->maxSizeBytes) {
            throw new AppException(ErrorCodes::ERR_FILE_002, null, 422);
        }

        $binaryContent = (string) file_get_contents($file->getPathname());
        return $this->validate($binaryContent, $file->getClientOriginalName());
    }

    public function validate(string $binaryContent, string $claimedFilename): string
    {
        if (strlen($binaryContent) > $this->maxSizeBytes) {
            throw new AppException(ErrorCodes::ERR_FILE_002, null, 422);
        }

        $detected = false;
        foreach (self::ALLOWED_SIGNATURES as $signatures) {
            foreach ($signatures as $sig) {
                if (str_starts_with($binaryContent, $sig)) {
                    $detected = true;
                    break 2;
                }
            }
        }
        if (!$detected) {
            throw new AppException(ErrorCodes::ERR_FILE_001, null, 422);
        }

        return $this->sanitizeFilename($claimedFilename);
    }

    public function sanitizeFilename(string $filename): string
    {
        $safeName = preg_replace('/[^a-zA-Z0-9._-]/', '', basename($filename));
        if (empty($safeName) || str_contains($safeName, '..')) {
            throw new AppException(ErrorCodes::ERR_FILE_001, null, 422);
        }

        $ext = strtolower(pathinfo($safeName, PATHINFO_EXTENSION));
        if (!in_array($ext, self::ALLOWED_EXTENSIONS, true)) {
            throw new AppException(ErrorCodes::ERR_FILE_001, null, 422);
        }

        return bin2hex(random_bytes(16)) . '.' . $ext;
    }

    public function detectMimeType(string $filePath): string
    {
        $handle = fopen($filePath, 'rb');
        $header = fread($handle, 12);
        fclose($handle);

        foreach (self::ALLOWED_SIGNATURES as $mime => $signatures) {
            foreach ($signatures as $sig) {
                if (str_starts_with($header, $sig)) {
                    return $mime;
                }
            }
        }

        throw new AppException(ErrorCodes::ERR_FILE_001, 'نوع فایل پشتیبانی نمی‌شود', 422);
    }
}
