<?php

namespace App\Controller;

use App\Entity\Domain;
use App\Form\DomainType;
use App\Service\DataGrid\DataGrid;
use App\Service\DataGrid\DataGridFilterMultiselect;
use App\Service\DataGrid\DataGridFilterQuery;
use App\Service\DataGrid\DataGridHeader;
use App\Service\DataGrid\DataGridNavigation;
use App\Service\DataGrid\DataGridSort;
use App\Service\Domain\Builder;
use App\Service\DomainService;
use Doctrine\ORM\EntityNotFoundException;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\View\View;
use Limenius\Liform\Liform;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class DomainController extends FOSRestController
{
    private $domainService;
    /**
     * @var Builder
     */
    private $domainBuilder;

    public function __construct(DomainService $domainService, Builder $domainBuilder)
    {
        $this->domainService = $domainService;
        $this->domainBuilder = $domainBuilder;
    }

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

        $dataGrid
            ->setName('domain')
            ->addHeader(new DataGridHeader(['label' => 'URL', 'id' => 'url', 'isSortable' => true]))
            ->addHeader(new DataGridHeader(['label' => 'IP', 'id' => 'ip', 'isSortable' => true]))
            ->addHeader(new DataGridHeader(['label' => 'Last Checked At?', 'id' => 'checkedAt', 'isSortable' => true, 'type' => 'datetime']));

        $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->addFilter(new DataGridFilterMultiselect(
            'ip',
            [],
            $request->get('ip') ?? []));

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

        // In case our GET was a success we need to return a 200 HTTP OK response with the collection of article object
        return View::create($dataGrid, Response::HTTP_OK);
    }

    /**
     * Retrieves a collection of Domain resource.
     *
     * @Rest\Get("/domain")
     *
     * @return View
     */
    public function getAllDomains(): View
    {
        $domains = $this->domainService->getAllDomains();

        // In case our GET was a success we need to return a 200 HTTP OK response with the collection of article object
        return View::create($domains, Response::HTTP_OK);
    }

    /**
     * Retrieves an domain resource.
     *
     * @Rest\Get("/domain/{domainId}", requirements={"domainId"="\d+"}))
     *
     * @param int $domainId
     *
     * @return View
     */
    public function getDomain(int $domainId): View
    {
        try {
            $domain = $this->domainService->getDomain($domainId);

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

    /**
     * Creates an domain resource.
     *
     * @Rest\Post("/domain")
     *
     * @param Request $request
     *
     * @return View
     */
    public function postDomain(Request $request): View
    {
        $form = $this->createForm(DomainType::class, null, ['csrf_protection' => false]);

        $form->submit($request->request->all());

        if ($form->isSubmitted() && $form->isValid()) {
            try {
                $this->domainBuilder->setUrl($form->getData()->getUrl());
                $domainTemp = $this->domainBuilder->build();
            } catch (\Throwable $exception) {
                return View::create($exception->getMessage(), Response::HTTP_BAD_REQUEST);
            }

            $formDomain = $form->getData();
            $formDomain->setUrl($domainTemp->getUrl());
            $formDomain->setProtocol($domainTemp->getProtocol());
            $formDomain->setUrlType($domainTemp->getUrlType());
            $domain = $this->domainService->saveDomain($formDomain);

            // In case our POST was a success we need to return a 201 HTTP CREATED response
            return View::create($domain, Response::HTTP_CREATED);
        }

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

    /**
     * Replaces Domain resource.
     *
     * @Rest\Put("/domain/{domainId}")
     *
     * @param Request $request
     * @param int     $domainId
     *
     * @return View
     */
    public function putDomain(Request $request, int $domainId): View
    {
        try {
            $domain = $this->domainService->getDomain($domainId);
        } catch (EntityNotFoundException $e) {
            return View::create(['code' => Response::HTTP_NOT_FOUND, 'message' => $e->getMessage()], Response::HTTP_NOT_FOUND);
        }

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

        if ($form->isSubmitted() && $form->isValid()) {
            $domain = $this->domainService->saveDomain($form->getData());

            // In case our POST was a success we need to return a 201 HTTP CREATED response
            return View::create($domain, Response::HTTP_OK);
        }

        // In case our PUT was a success we need to return a 200 HTTP OK response with the object as a result of PUT
        return View::create($form, Response::HTTP_BAD_REQUEST);
    }

    /**
     * Updates Domain resource.
     *
     * @Rest\Patch("/domain/{domainId}")
     *
     * @param Request $request
     * @param int     $domainId
     *
     * @return View
     */
    public function patchDomainAction(Request $request, int $domainId): View
    {
        try {
            $domain = $this->domainService->getDomain($domainId);
        } catch (EntityNotFoundException $e) {
            return View::create(['code' => Response::HTTP_NOT_FOUND, 'message' => $e->getMessage()], Response::HTTP_NOT_FOUND);
        }

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

        if ($form->isSubmitted() && $form->isValid()) {
            $domain = $this->domainService->saveDomain($form->getData());

            // In case our PUT was a success we need to return a 200 HTTP OK response with the object as a result of PATCH
            return View::create($domain, Response::HTTP_OK);
        }

        // In case our PATCH wasn't a success we need to return a 400 HTTP BAD REQUEST response with the form object as a result
        return View::create($form, Response::HTTP_BAD_REQUEST);
    }

    /**
     * Removes the Domain resource.
     *
     * @Rest\Delete("/domain/{domainId}")
     *
     * @param int $domainId
     *
     * @return View
     */
    public function deleteDomain(int $domainId): View
    {
        try {
            $domain = $this->domainService->getDomain($domainId);
        } catch (EntityNotFoundException $e) {
            return View::create(['code' => Response::HTTP_BAD_REQUEST, 'message' => $e->getMessage()], Response::HTTP_BAD_REQUEST);
        }

        $this->domainService->deleteDomain($domain);
        // In case our DELETE was a success we need to return a 204 HTTP NO CONTENT response. The object is deleted.
        return View::create([], Response::HTTP_NO_CONTENT);
    }

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

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

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

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