<?php

namespace AutoAdmin\Bundle\Admin\Controller\Admin\Traits;

use AutoAdmin\Bundle\Admin\Builder\LayoutBuilder;
use AutoAdmin\Bundle\Admin\Builder\TableBuilder;
use AutoAdmin\Bundle\Admin\Exception\ForbiddenActionException;
use AutoAdmin\Bundle\Admin\Exception\UnknownActionException;
use AutoAdmin\Bundle\Admin\Exception\UnknownEntityException;
use AutoAdmin\Bundle\Admin\Form\ExportType;
use AutoAdmin\Bundle\Admin\Model\EntityFilterModel;
use AutoAdmin\Bundle\Content\Model\PublishableInterface;
use AutoAdmin\Bundle\Content\Model\PublishTimePeriodInterface;
use AutoAdmin\Bundle\Content\Model\SpamInterface;
use DateTime;
use Doctrine\ORM\QueryBuilder;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Exception;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use ReflectionClass;
use ReflectionException;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

trait ListTrait
{
    /**
     * @throws UnknownEntityException
     * @throws UnknownActionException
     * @throws ForbiddenActionException
     * @throws \Exception
     */
    public function list(string $entitySlug, int $page, Request $request): Response
    {
        $this->ensureActionIsAllowed($entitySlug, 'list');

        /** @var TableBuilder $tableBuilder */
        $tableBuilder = $this->builderFactory->get('table');

        /** @var LayoutBuilder $topbarBuilder */
        $topbarBuilder = $this->builderFactory->get('layout');
        $config = $this->getEntityConfigFromRequest($request);

        // todo: rework filter <> table builder relation to un-couple code
        $searchData = $tableBuilder->buildSearchConfigArray($config);
        $filter = new EntityFilterModel($request, $searchData);

        $isSearching = $filter->isSearching();
        $quickFilters = $this->quickFilters($entitySlug, $request, $filter, $config);
        $activeFilter = array_filter($quickFilters, function ($filter) {
            return $filter['active'] === true;
        });
        $filter = array_shift($activeFilter)['filter'] ?? $filter;
        $entities = $this->entityFinder->find($filter, $config);

        $table = $tableBuilder->buildEntityDatatableArray($config, $filter, $entities);
        $table = array_merge($table, ['search' => $searchData, 'isSearching' => $isSearching]);

        $headActions = $topbarBuilder->buildTopbarActions($entitySlug, $request->get('action'));
        $headActions = $this->filterActions($headActions, $request);

        // render export form
        $fields = array_unique($config['list']['fields'],SORT_REGULAR);

        $exportForm = $this->createForm(ExportType::class, null, [
            'data_fields' => $fields,
        ]);

        $exportForm->handleRequest($request);

        if ($exportForm->isSubmitted() && $exportForm->isValid()) {
            $data = $exportForm->getData();

            $filtered = [];

            foreach ($data['fields'] as $fieldKey => $field) {
                $filter = array_filter($fields, function ($fieldConfig) use ($field) {
                    return $fieldConfig['property'] === $field;
                });
                $filtered[] = $filter[array_key_first($filter)];
            }

            // if enable for this entity, export to excel
            return new BinaryFileResponse(
                $this->exportFields(
                    empty($data['fields']) ? $fields : $filtered,
                    $config['class'],
                    $entities
                )
            );
        }

        return $this->listHook(
            $this->getTemplate('list'),
            [
                'exportForm' => $exportForm->createView(),
                'entityName' => $this->entityHelper->getEntityNameFromSlug($entitySlug),
                'headActions' => $headActions,
                'groupActions' => $config['list']['group_actions'],
                'entityArray' => $table,
                'quickFilters' => $quickFilters,
            ]
        );
    }

    public function listHook(string $template, array $vars): Response
    {
        return $this->render($template, $vars);
    }

    protected function filterActions($actions, Request $request): array
    {
        $routeNameExpl = explode('_', $request->get('_route'));
        $lastRouteNameItem = end($routeNameExpl);

        return array_filter(
            $actions,
            function ($el) use ($lastRouteNameItem) {
                if ($el['action'] === $lastRouteNameItem) {
                    return false;
                }

                return true;
            }
        );
    }

    protected function quickFilters(string $entitySlug, Request $request, EntityFilterModel $filter, array $config)
    {
        $quickFilters = [];
        $reflectionClass = new ReflectionClass($this->entityHelper->getEntityClassFromSlug($entitySlug));

        if (
            $reflectionClass->implementsInterface(PublishableInterface::class)
            && $reflectionClass->implementsInterface(PublishTimePeriodInterface::class)
        ) {
            $quickFilters[] = $this->quickFilter(
                'Publié',
                'published',
                function (QueryBuilder $qb) {
                    $qb->andWhere('_.publishable = 1');
                    $qb->andWhere($qb->expr()->orX(
                        $qb->expr()->isNull('_.publishStartDate'),
                        $qb->expr()->lte('_.publishStartDate', ':now')
                    ));
                    $qb->setParameter('now', new DateTime());
                },
                $request,
                $config
            );
        }

        if ($reflectionClass->implementsInterface(PublishableInterface::class)) {
            $quickFilters[] = $this->quickFilter(
                'Brouillon',
                'draft',
                function (QueryBuilder $qb) {
                    $qb->andWhere('_.publishable = 0');
                },
                $request,
                $config
            );
        }

        if ($reflectionClass->implementsInterface(SpamInterface::class)) {
            $quickFilters[] = $this->quickFilter(
                'Spam',
                'spam',
                function (QueryBuilder $qb) {
                    $qb->andWhere('_.isSpam = 1');
                },
                $request,
                $config
            );
        }

        if ($reflectionClass->implementsInterface(PublishTimePeriodInterface::class)) {
            $quickFilters[] = $this->quickFilter(
                'Programmé',
                'programmed',
                function (QueryBuilder $qb) {
                    $qb->andWhere('_.publishable = 1');
                    $qb->andWhere('_.publishStartDate IS NOT NULL');
                    $qb->andWhere('_.publishStartDate > :now');
                    $qb->setParameter('now', new DateTime());
                },
                $request,
                $config
            );
        }

        array_unshift($quickFilters, $this->quickFilterAll(
            'Tous',
            $request,
            $filter,
            $config
        ));

        return $quickFilters;
    }

    protected function quickFilterAll(string $label, Request $request, EntityFilterModel $filter, array $config)
    {
        return [
            'label' => $label,
            'link' => Request::create($request->getUri(), $request->getMethod(), ['quickFilter' => null])->getRequestUri(),
            'count' => $this->entityFinder->find($filter, $config, true),
            'active' => !$request->query->has('quickFilter') || !$request->query->get('quickFilter'),
        ];
    }

    protected function quickFilter(string $label, string $slug, callable $callback, Request $request, array $config)
    {
        $filter = new EntityFilterModel($request);
        $filter->addQbAlterateCallback($callback);

        return [
            'label' => $label,
            'link' => Request::create($request->getUri(), $request->getMethod(), ['quickFilter' => $slug])->getRequestUri(),
            'count' => $this->entityFinder->find($filter, $config, true),
            'active' => $request->query->has('quickFilter') && $request->query->get('quickFilter') === $slug,
            'filter' => $filter
        ];
    }

    /**
     * get file path for export
     * @throws ReflectionException
     * @throws Exception
     */
    protected function exportFields(array $fields, string $entitiyClass, array $rows): string
    {
        $reflection = new ReflectionClass($entitiyClass);

        $spreadsheet = new Spreadsheet();
        $sheet = $spreadsheet->getActiveSheet();
        $sheet->setTitle('Export ' . $reflection->getShortName());

        $spreadsheet = new Spreadsheet();
        $sheet = $spreadsheet->getActiveSheet();

        $data = [];
        $_data = [];

        foreach ($fields as $field) {
            $_data[] = $field['label'];
        }
        $data[] = $_data;

        foreach ($rows as $row) {
            $_data = [];
            foreach ($fields as $field) {
                $method = 'get' . ucfirst($field['property']);
                $_data[] = $row->{$method}();
            }
            $data[] = $_data;
        }

        $sheet->fromArray($data);

        $writer = new Xlsx($spreadsheet);

        $fileName = $reflection->getShortName() . ' ' . date('Y-m-d H:i:s');

        $file = sprintf(
            'export/' . $reflection->getShortName() . '/%s.xlsx',
            $fileName
        );

        $url = 'export/' . $reflection->getShortName() . '/';

        if (!is_dir($url)) {
            mkdir($url, 0777, true);
        }

        $writer->save($file);

        return $file;
    }
}
