<?php

namespace App\Controller;

use App\Entity\File;
use App\Form\FileType;
use App\Service\DataGrid\DataGrid;
use App\Service\DataGrid\DataGridFilterQuery;
use App\Service\DataGrid\DataGridHeader;
use App\Service\DataGrid\DataGridNavigation;
use App\Service\DataGrid\DataGridSort;
use App\Service\File\Namer\NamerInterface;
use App\Service\FileService;
use Doctrine\ORM\EntityNotFoundException;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\View\View;
use League\Flysystem\FileExistsException;
use League\Flysystem\Filesystem;
use Limenius\Liform\Liform;
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class FileController extends FOSRestController
{
    private $service;
    private $filesystem;
    private $namer;

    public function __construct(FileService $service, Filesystem $filesystem, NamerInterface $namer)
    {
        $this->service = $service;
        $this->filesystem = $filesystem;
        $this->namer = $namer;
    }

    /**
     * Retrieves a collection of File resource.
     *
     * @Rest\Get("/file/grid")
     * @Rest\View(serializerGroups={"grid"})
     *
     * @param Request $request
     *
     * @return View
     */
    public function getGridAction(Request $request): View
    {
        $dataGrid = new DataGrid();

        $dataGrid
            ->setName('File')
            ->addHeader(new DataGridHeader(['label' => 'URL', 'id' => 'url', 'isSortable' => true]));

        $dataGrid->setNavigation(new DataGridNavigation(['page' => $request->get('page'), 'rpp' => $request->get('rpp')]));

        $dataGrid->addSorter(new DataGridSort(['sort' => $request->get('sort'), 'order' => $request->get('order')]));

        $dataGrid->addFilter(new DataGridFilterQuery($request->get('q')));

        $dataGrid->setData($this->service->getByGrid($dataGrid))
            ->setTotal($this->service->countByGrid($dataGrid));

        return View::create($dataGrid, Response::HTTP_OK);
    }

    /**
     * Retrieves a collection of File resource.
     *
     * @Rest\Get("/file")
     *
     * @return View
     */
    public function getAllAction(): View
    {
        $items = $this->service->getAll();

        return View::create($items, Response::HTTP_OK);
    }

    /**
     * Retrieves an File resource.
     *
     * @Rest\Get("/file/{id}", requirements={"id"="\d+"}))
     *
     * @param int $id
     *
     * @return View
     */
    public function getAction(int $id): View
    {
        try {
            $file = $this->service->get($id);

            return View::create($file, Response::HTTP_OK);
        } catch (EntityNotFoundException $e) {
            return View::create(['code' => Response::HTTP_NOT_FOUND, 'message' => $e->getMessage()], Response::HTTP_NOT_FOUND);
        }
    }

    /**
     * Creates an File resource.
     *
     * @Rest\Post("/file")
     *
     * @param Request $request
     *
     * @return View
     */
    public function postAction(Request $request): View
    {
        if (0 === $request->files->count()) {
            return View::create(['code' => Response::HTTP_BAD_REQUEST, 'message' => (new FileNotFoundException())->getMessage()], Response::HTTP_BAD_REQUEST);
        }

        $file = $request->files->get('file');

        if (!$file->isValid()) {
            View::create(['code' => Response::HTTP_BAD_REQUEST, 'message' => 'File has not been uploded'], Response::HTTP_BAD_REQUEST);
        }

        $name = $this->namer->getName($file);

        $year = date('Y', time());
        $month = date('m', time());

        try {
            $stream = fopen($file->getRealPath(), 'r+');
            $this->filesystem->writeStream($year.'/'.$month.'/'.$name, $stream);
            fclose($stream);
        } catch (FileExistsException $e) {
            View::create(['code' => Response::HTTP_BAD_REQUEST, 'message' => 'File has not been uploded'], Response::HTTP_BAD_REQUEST);
        }

        $fileEntity = (new File())
            ->setTitle($file->getClientOriginalName())
            ->setFilename($name)
            ->setPath($year.'/'.$month.'/')
            ->setMimetype($file->getMimeType())
            ->setSize($file->getSize());

        $file = $this->service->save($fileEntity);

        return View::create($file, Response::HTTP_OK);
    }

    /**
     * Replaces File resource.
     *
     * @Rest\Put("/file/{id}")
     *
     * @param Request $request
     * @param int     $id
     *
     * @return View
     */
    public function putAction(Request $request, int $id): View
    {
        try {
            $file = $this->service->get($id);
        } catch (EntityNotFoundException $e) {
            return View::create(['code' => Response::HTTP_NOT_FOUND, 'message' => $e->getMessage()], Response::HTTP_NOT_FOUND);
        }

        $form = $this->createForm(FileType::class, $file, ['csrf_protection' => false]);
        $form->submit($request->request->all());

        if ($form->isSubmitted() && $form->isValid()) {
            $file = $this->service->save($form->getData());

            return View::create($file, Response::HTTP_OK);
        }

        return View::create($form, Response::HTTP_BAD_REQUEST);
    }

    /**
     * Updates File resource.
     *
     * @Rest\Patch("/file/{id}")
     *
     * @param Request $request
     * @param int     $id
     *
     * @return View
     */
    public function patchAction(Request $request, int $id): View
    {
        try {
            $file = $this->service->get($id);
        } catch (EntityNotFoundException $e) {
            return View::create(['code' => Response::HTTP_NOT_FOUND, 'message' => $e->getMessage()], Response::HTTP_NOT_FOUND);
        }

        $form = $this->createForm(FileType::class, $file, ['csrf_protection' => false]);
        $form->submit($request->request->all(), false);

        if ($form->isSubmitted() && $form->isValid()) {
            $file = $this->service->save($form->getData());

            return View::create($file, Response::HTTP_OK);
        }

        return View::create($form, Response::HTTP_BAD_REQUEST);
    }

    /**
     * Removes the File resource.
     *
     * @Rest\Delete("/file/{id}")
     *
     * @param int $id
     *
     * @return View
     */
    public function deleteAction(int $id): View
    {
        try {
            $file = $this->service->get($id);
        } catch (EntityNotFoundException $e) {
            return View::create(['code' => Response::HTTP_BAD_REQUEST, 'message' => $e->getMessage()], Response::HTTP_BAD_REQUEST);
        }

        $this->service->delete($file);

        return View::create([], Response::HTTP_NO_CONTENT);
    }

    /**
     * Return serialized form to create resource.
     *
     * @Rest\Get("/file/schema")
     *
     * @param Liform $liform
     *
     * @return View
     */
    public function getSchemaAction(Liform $liform): View
    {
        $file = new File();

        $form = $this->createForm(FileType::class, $file, ['csrf_protection' => false]);

        $form = $liform->transform($form);

        return View::create($form, Response::HTTP_OK);
    }
}
