<?php
namespace Zeedhi\Framework\File;

use Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesserInterface;

class TypeValidator {

    /** @var ExtensionGuesserInterface $mimeTypeGuesser */
    protected $mimeTypeGuesser;

    public function __construct(ExtensionGuesserInterface $mimeTypeGuesser) {
        $this->mimeTypeGuesser = $mimeTypeGuesser;
    }

    /**
     * @param string $fileName
     *
     * @return string
     */
    private function getFileExtension(string $fileName) {
        $dotPosition = strrpos($fileName, '.');
        return $dotPosition ? substr($fileName, $dotPosition + 1) : '';
    }

    /**
     * @param array $file
     * @param array $acceptedExtensions
     *
     * @return bool
     */
    private function isExtensionValid(array $file, array $acceptedExtensions) {
        return $this->isValidExtension($file['name'], $acceptedExtensions);
    }
    
    /**
     * @param array $file
     * @param array $acceptedExtensions
     *
     * @return bool
     */
    private function isValidExtension(string $fileName, array $acceptedExtensions) {
        $fileExtension = $this->getFileExtension($fileName);
        return strlen($fileExtension) === 0 || empty($acceptedExtensions) || in_array($fileExtension, $acceptedExtensions);
    }

    /**
     * @param array $file
     * @return mixed
     */
    private function getMimeTypeFromBase64(array $file) {
        return str_replace('data:', '', explode(';base64,', $file['b64File'])[0]);
    }

    /**
     * @param array $file
     * @param array $acceptedMimeTypes
     *
     * @return bool
     */
    private function isMimeTypeValid(array $file, array $acceptedMimeTypes) {
        $fileDataMimeType = $file['type'];
        $mimeTypeFromBase64 = $this->getMimeTypeFromBase64($file);
        return (empty($acceptedMimeTypes) || in_array($fileDataMimeType, $acceptedMimeTypes))
            && $fileDataMimeType === $mimeTypeFromBase64;
    }

    /**
     * @param string $mimeType
     * @param array $acceptedMimeTypes
     *
     * @return bool
     */
    private function isValidMimeType(string $mimeType, array $acceptedMimeTypes) {
        return strlen($mimeType) !== 0 && (empty($acceptedMimeTypes) || in_array($mimeType, $acceptedMimeTypes));
    }

    private function doesExtensionMatchesMimeType(string $fileName, string $mimeType) {
        $fileExtension = $this->getFileExtension($fileName);
        $guessedExtension = $this->mimeTypeGuesser->guess($mimeType);
        return $guessedExtension === $fileExtension;
    }

    /**
     * Validate that file is accepted by mimeTypes and extensions.
     *
     * Check if data provided in parameter $file belongs to the chosen
     * list of mime types and the chosen list of extensions.
     *
     * @param array    $file               The file data, must has the keys 'type', 'b64File' and 'name'
     * @param string[] $acceptedMimeTypes  This is a list of mimeTypes to be accepted.
     * @param string[] $acceptedExtensions This is a list of extensions to be accepted.
     *
     * @return boolean True if valid, false otherwise.
     */
    public function isValid(array $file, array $acceptedMimeTypes, array $acceptedExtensions) {
        return $this->isExtensionValid($file, $acceptedExtensions)
            && $this->isMimeTypeValid($file, $acceptedMimeTypes)
            && $this->doesExtensionMatchesMimeType($file['name'], $file['type']);
    }

    /**
     * Validate that file is accepted by mimeTypes and extensions.
     *
     * Check if data provided in parameter $file belongs to the chosen
     * list of mime types and the chosen list of extensions.
     *
     * @param array    $file               The file data, must has the keys 'type', 'b64File' and 'name'
     * @param string[] $acceptedMimeTypes  This is a list of mimeTypes to be accepted.
     * @param string[] $acceptedExtensions This is a list of extensions to be accepted.
     *
     * @return boolean True if valid, false otherwise.
     */
    public function isValidForDecodedFile(string $fileName, string $fileType, array $acceptedMimeTypes, array $acceptedExtensions) {
        return $this->isValidExtension($fileName, $acceptedExtensions)
            && $this->isValidMimeType($fileType, $acceptedMimeTypes)
            && $this->doesExtensionMatchesMimeType($fileName, $fileType);
    }
}