<?php

namespace App\Controller;

use App\Entity\TimeLog;
use App\Entity\TimeLogTypeCategory;
use App\Form\TimeLogType;
use App\Service\DataGrid\DataGrid;
use App\Service\DataGrid\DataGridAggregate;
use App\Service\DataGrid\DataGridFilterDateRange;
use App\Service\DataGrid\DataGridFilterMultiselect;
use App\Service\DataGrid\DataGridHeader;
use App\Service\DataGrid\DataGridNavigation;
use App\Service\DataGrid\DataGridSort;
use App\Service\TimeLogService;
use App\Service\TimeLogTypeCategoryService;
use App\Service\TimeLogTypeService;
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 TimeLogController extends FOSRestController
{
    private $service;

    public function __construct(TimeLogService $service)
    {
        $this->service = $service;
    }

    /**
     * Retrieves a collection of TimeLog resource.
     *
     * @Rest\Get("/timelog/grid")
     * @Rest\View(serializerGroups={"grid"})
     *
     * @param Request                    $request
     * @param TimeLogTypeService         $timeLogTypeService
     * @param TimeLogTypeCategoryService $timeLogTypeCategoryService
     *
     * @return View
     *
     * @throws \Exception
     */
    public function getGridAction(Request $request, TimeLogTypeService $timeLogTypeService,
                                  TimeLogTypeCategoryService $timeLogTypeCategoryService): View
    {
        $dataGrid = new DataGrid();

        $timeLogTypes = $timeLogTypeService->getAll();
        $types = array_map(function ($type) {
            return ['label' => ucfirst($type->getTitle()), 'value' => $type->getId()];
        }, $timeLogTypes);

        $timeLogTypeCategories = $timeLogTypeCategoryService->getAll();
        $categories = array_map(function (TimeLogTypeCategory $category) {
            return ['label' => ucfirst($category->getTitle()), 'value' => $category->getId()];
        }, $timeLogTypeCategories);

        $dataGrid
            ->setName('Time Log Content')
            ->addHeader(new DataGridHeader(['label' => 'Campaign', 'id' => 'campaign', 'isSortable' => true, 'type' => 'campaign']))
            ->addHeader(new DataGridHeader(['label' => 'Subject', 'id' => 'subject', 'isSortable' => false, 'type' => 'subject']))
            ->addHeader(new DataGridHeader(['label' => 'Subject Type', 'id' => 'subjectType', 'isSortable' => true]))
            ->addHeader(new DataGridHeader(['label' => 'User', 'id' => 'user', 'isSortable' => true, 'type' => 'user']))
            ->addHeader(new DataGridHeader(['label' => 'Type', 'id' => 'type', 'isSortable' => true, 'type' => 'timelogtype']))
            ->addHeader(new DataGridHeader(['label' => 'Date From', 'id' => 'dateFrom', 'isSortable' => true, 'type' => 'date']))
            ->addHeader(new DataGridHeader(['label' => 'Time Spent', 'id' => 'timeSpent', 'isSortable' => true, 'type' => 'time']));

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

        if ($request->get('sort')) {
            $dataGrid->addSorter(new DataGridSort(['sort' => $request->get('sort'), 'order' => $request->get('order')]));
        } else {
            $dataGrid->addSorter(new DataGridSort(['sort' => 'createdAt', 'order' => 'DESC']));
        }

        $dataGrid->addFilter(new DataGridFilterMultiselect(
            'campaign',
            [],
            $request->get('campaign') ?? []));

        $subjects = $this->service->getSubjects();
        $subjectOptions = array_map(function ($subject) {
            return ['label' => ucfirst($subject['subjectType']), 'value' => $subject['subjectType']];
        }, $subjects);

        $dataGrid->addFilter(new DataGridFilterMultiselect(
            'subjectType',
            $subjectOptions,
            $request->get('subjectType') ?? []));

        $dataGrid->addFilter(new DataGridFilterMultiselect(
            'subjectId',
            [],
            $request->get('subjectId') ?? []));

        $dataGrid->addFilter(new DataGridFilterMultiselect(
            'type',
            $types,
            $request->get('type') ?? []));

        $dataGrid->addFilter(new DataGridFilterMultiselect(
            'category',
            $categories,
            $request->get('category') ?? []));

        $dataGrid->addFilter(new DataGridFilterMultiselect(
            'client',
            [],
            $request->get('client') ?? []));

        $dataGrid->addFilter(new DataGridFilterMultiselect(
            'user',
            [],
            $request->get('user') ?? []));

        $startDate = !empty($request->get('dateFrom')[0]) ? $request->get('dateFrom')[0] :
            $this->service->getFirstDate();
        $endDate = !empty($request->get('dateFrom')[1]) ? $request->get('dateFrom')[1] : date('Y-m-d', time());
        $startDate = (new \DateTime($startDate))->setTime(0, 0, 0)->format('Y-m-d H:i:s');
        $endDate = (new \DateTime($endDate))->setTime(23, 59, 59)->format('Y-m-d H:i:s');
        $dataGrid->addFilter(new DataGridFilterDateRange('dateFrom', [], [$startDate, $endDate]));

        if ($request->get('aggr')) {
            foreach ($request->get('aggr') as $id) {
                $value = $request->get($id);
                $params = explode(':', $value);
                if (2 === count($params)) {
                    list($field, $function) = $params;
                    $groupBy = '';
                } elseif (3 === count($params)) {
                    list($field, $function, $groupBy) = $params;
                }
                $dataGrid->addAggregate(new DataGridAggregate($id, $function, $field, $groupBy));
            }
        }

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

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

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

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

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

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

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

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

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

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

    /**
     * Replaces TimeLog resource.
     *
     * @Rest\Put("/timelog/{id}")
     *
     * @param Request $request
     * @param int     $id
     *
     * @return View
     */
    public function putAction(Request $request, int $id): View
    {
        try {
            $time_log_content = $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(TimeLogType::class, $time_log_content, ['csrf_protection' => false]);
        $form->submit($request->request->all());

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

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

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

    /**
     * Updates TimeLog resource.
     *
     * @Rest\Patch("/timelog/{id}")
     *
     * @param Request $request
     * @param int     $id
     *
     * @return View
     */
    public function patchAction(Request $request, int $id): View
    {
        try {
            $time_log_content = $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(TimeLogType::class, $time_log_content, ['csrf_protection' => false]);
        $form->submit($request->request->all(), false);

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

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

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

    /**
     * Removes the TimeLog resource.
     *
     * @Rest\Delete("/timelog/{id}")
     *
     * @param int $id
     *
     * @return View
     */
    public function deleteAction(int $id): View
    {
        try {
            $time_log_content = $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($time_log_content);

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

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

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

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

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