<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Http\UploadedFile;

/**
 * Server-side guard for anonymous enquiry-form file uploads.
 *
 * Enforced on every file_upload question regardless of the per-question tenant config (which may be
 * empty). Restricts uploads to the content MIME types allowed for media (config media.media_types)
 * and caps the size, closing the anonymous arbitrary-upload / stored-XSS vector (AUTO-1689).
 */
class EnquiryFileUpload implements ValidationRule
{
    private const MAX_SIZE_DEFAULT = 10 * 1024 * 1024;

    private const MAX_SIZE_VIDEO = 50 * 1024 * 1024;

    /**
     * Run the validation rule.
     *
     * @param  \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString  $fail
     */
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        if (!$value instanceof UploadedFile || !$value->isValid()) {
            // A non-file / failed upload is reported by the accompanying `file` rule.
            return;
        }

        $mimeType = $value->getMimeType();

        if (!in_array($mimeType, $this->allowedMimeTypes(), true)) {
            $fail('validation.enquiry.file_type')->translate();
            return;
        }

        $maxSize = $this->isVideo($mimeType) ? self::MAX_SIZE_VIDEO : self::MAX_SIZE_DEFAULT;

        if ($value->getSize() > $maxSize) {
            $fail('validation.enquiry.file_size')->translate([
                'max' => $this->humanReadableSize($maxSize),
            ]);
        }
    }

    /**
     * Flattened allowlist of content MIME types accepted for media uploads.
     *
     * @return array<int, string>
     */
    private function allowedMimeTypes(): array
    {
        return collect(config('media.media_types', []))
            ->flatten()
            ->filter()
            ->unique()
            ->values()
            ->all();
    }

    private function isVideo(?string $mimeType): bool
    {
        return in_array($mimeType, (array) config('media.media_types.video', []), true);
    }

    private function humanReadableSize(int $bytes): string
    {
        return (int) round($bytes / (1024 * 1024)) . 'MB';
    }
}
