<?php

/*
 * Copyright (C) 2015 IRSTEA
 * All rights reserved.
 */

namespace Irstea\FileUploadBundle\Entity\Repository;

use DateTime;
use Doctrine\ORM\EntityRepository;
use Gaufrette\Filesystem;
use Irstea\FileUploadBundle\Entity\UploadedFile;
use Irstea\FileUploadBundle\Event\FileUploadCompleteEvent;
use Irstea\FileUploadBundle\Exception\RejectedFileException;
use Irstea\FileUploadBundle\FileUploadEvents;
use Irstea\FileUploadBundle\Model\FileManagerInterface;
use Irstea\FileUploadBundle\Model\UploadedFileInterface;
use Psr\Log\LogLevel;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * Description of UploadedFileRepository
 *
 * @author Guillaume Perréal <guillaume.perreal@irstea.fr>
 */
class UploadedFileRepository extends EntityRepository implements FileManagerInterface
{
    use \Psr\Log\LoggerAwareTrait;

    /**
     * @var Filesystem
     */
    protected $filesystem;

    /**
     * @var EventDispatcher
     */
    protected $eventDispatcher;

    /**
     *
     * @param Filesystem $filesystem
     */
    public function setFilesystem(Filesystem $filesystem)
    {
        $this->filesystem = $filesystem;
    }

    /**
     *
     * @param EventDispatcherInterface $eventDispatcher
     */
    public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
    {
        $this->eventDispatcher = $eventDispatcher;
    }

    public function create($filename, $size, $mimeType, $lastModified = null, $createdBy = null, $createdFrom = null)
    {
        $file = new UploadedFile($createdBy, $createdFrom);

        $file
            ->setFilesystem($this->filesystem)
            ->setDisplayName($filename)
            ->setMetadata(
                [
                    'client' => [
                        'filename'     => $filename,
                        'size'         => $size,
                        'mimeType'     => $mimeType,
                        'lastModified' => $lastModified
                    ]
                ]
            );

        $this->_em->persist($file);
        $this->_em->flush();

        $this->log(LogLevel::INFO, 'File created', ['file' => $file]);

        return $file;
    }

    public function delete(UploadedFileInterface $file)
    {
        $this->_em->remove($file);
        $this->_em->flush();

        $this->log(LogLevel::INFO, 'File deleted', ['file' => $file]);
    }

    public function get($id)
    {
        if(!$id) {
            return null;
        }
        if(!is_string($id) || !\Rhumsaa\Uuid\Uuid::isValid($id)) {
            throw new \InvalidArgumentException(sprintf("Identifiant invalide: %s", (string)$id));
        }
        return $this->findOneById($id);
    }

    public function completed(UploadedFileInterface $file)
    {
        $path = $file->getPath();
        $fs = $this->filesystem;

        $file
            ->setChecksum($fs->checksum($path))
            ->setSize($fs->size($path))
            ->setMimeType($fs->mimeType($path))
            ->setEtat(UploadedFileInterface::ETAT_ORPHELIN);

        $this->_em->persist($file);

        try {
            $this->eventDispatcher->dispatch(FileUploadEvents::UPLOAD_COMPLETE, new FileUploadCompleteEvent($file));

            $this->_em->flush();
            $this->log(LogLevel::INFO, 'File completed', ['file' => $file]);

        } catch(RejectedFileException $ex) {
            $file->setEtat(UploadedFileInterface::ETAT_REJETE);

            $this->_em->flush();
            $this->log(LogLevel::WARNING, 'File rejected', ['file' => $file, 'exception' => $ex]);

            throw $ex;
        }
    }

    /**
     *
     * @param string $level
     * @param string $message
     * @param array $context
     */
    protected function log($level, $message, array $context = [])
    {
        if(null !== $this->logger) {
            $this->logger->log($level, $message, $context);
        }
    }

    public function findGarbage()
    {
        $files = $this->findBy(['etat' => [UploadedFileInterface::ETAT_EN_COURS, UploadedFileInterface::ETAT_ORPHELIN]]);

        $limit = new DateTime('now');
        $limit->modify('- 1 hour');

        return array_filter(
            $files,
            function(UploadedFileInterface $file) use($limit) {
                $mtime = $file->getLastModified();
                return $mtime === null || $mtime < $limit;
            }
        );
    }

    public function findFilesToValidate()
    {
        return $this->findBy(['etat' => [UploadedFileInterface::ETAT_ORPHELIN, UploadedFileInterface::ETAT_NORMAL]]);
    }

}