<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\MainBundle\Export\Formatter;

use Chill\MainBundle\Export\ExportManager;
use Chill\MainBundle\Export\FormatterInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Contracts\Translation\TranslatorInterface;
use function count;

// command to get the report with curl : curl --user "center a_social:password" "http://localhost:8000/fr/exports/generate/count_person?export[filters][person_gender_filter][enabled]=&export[filters][person_nationality_filter][enabled]=&export[filters][person_nationality_filter][form][nationalities]=&export[aggregators][person_nationality_aggregator][order]=1&export[aggregators][person_nationality_aggregator][form][group_by_level]=country&export[submit]=&export[_token]=RHpjHl389GrK-bd6iY5NsEqrD5UKOTHH40QKE9J1edU" --globoff

/**
 * Create a CSV List for the export.
 */
class CSVListFormatter implements FormatterInterface
{
    protected $exportAlias;

    protected $exportData;

    /**
     * @var ExportManager
     */
    protected $exportManager;

    protected $formatterData;

    /**
     * This variable cache the labels internally.
     *
     * @var string[]
     */
    protected $labelsCache;

    protected $result;

    /**
     * @var TranslatorInterface
     */
    protected $translator;

    public function __construct(TranslatorInterface $translatorInterface, ExportManager $exportManager)
    {
        $this->translator = $translatorInterface;
        $this->exportManager = $exportManager;
    }

    /**
     * build a form, which will be used to collect data required for the execution
     * of this formatter.
     *
     * @uses appendAggregatorForm
     *
     * @param type $exportAlias
     */
    public function buildForm(
        FormBuilderInterface $builder,
        $exportAlias,
        array $aggregatorAliases,
    ): void {
        $builder->add('numerotation', ChoiceType::class, [
            'choices' => [
                'yes' => true,
                'no' => false,
            ],
            'expanded' => true,
            'multiple' => false,
            'label' => 'Add a number on first column',
        ]);
    }

    public function getFormDefaultData(array $aggregatorAliases): array
    {
        return ['numerotation' => true];
    }

    public function getName()
    {
        return 'CSV vertical list';
    }

    /**
     * Generate a response from the data collected on differents ExportElementInterface.
     *
     * @param mixed[] $result          The result, as given by the ExportInterface
     * @param mixed[] $formatterData   collected from the current form
     * @param string  $exportAlias     the id of the current export
     * @param array   $filtersData     an array containing the filters data. The key are the filters id, and the value are the data
     * @param array   $aggregatorsData an array containing the aggregators data. The key are the filters id, and the value are the data
     *
     * @return Response The response to be shown
     */
    public function getResponse(
        $result,
        $formatterData,
        $exportAlias,
        array $exportData,
        array $filtersData,
        array $aggregatorsData,
    ): \Symfony\Component\HttpFoundation\Response {
        $this->result = $result;
        $this->exportAlias = $exportAlias;
        $this->exportData = $exportData;
        $this->formatterData = $formatterData;

        $output = fopen('php://output', 'wb');

        $this->prepareHeaders($output);

        $i = 1;

        foreach ($result as $row) {
            $line = [];

            if (true === $this->formatterData['numerotation']) {
                $line[] = $i;
            }

            foreach ($row as $key => $value) {
                $line[] = $this->getLabel($key, $value);
            }

            fputcsv($output, $line);

            ++$i;
        }

        $csvContent = stream_get_contents($output);
        fclose($output);

        $response = new Response();
        $response->setStatusCode(200);
        $response->headers->set('Content-Type', 'text/csv; charset=utf-8');
        // $response->headers->set('Content-Disposition','attachment; filename="export.csv"');

        $response->setContent($csvContent);

        return $response;
    }

    public function getType()
    {
        return FormatterInterface::TYPE_LIST;
    }

    /**
     * Give the label corresponding to the given key and value.
     *
     * @param string $key
     * @param string $value
     *
     * @return string
     *
     * @throws \LogicException if the label is not found
     */
    protected function getLabel($key, $value)
    {
        if (null === $this->labelsCache) {
            $this->prepareCacheLabels();
        }

        if (!\array_key_exists($key, $this->labelsCache)) {
            throw new \OutOfBoundsException(sprintf('The key "%s" is not present in the list of keys handled by this query. Check your `getKeys` and `getLabels` methods. Available keys are %s.', $key, \implode(', ', \array_keys($this->labelsCache))));
        }

        return $this->labelsCache[$key]($value);
    }

    /**
     * Prepare the label cache which will be used by getLabel. This function
     * should be called only once in the generation lifecycle.
     */
    protected function prepareCacheLabels()
    {
        $export = $this->exportManager->getExport($this->exportAlias);
        $keys = $export->getQueryKeys($this->exportData);

        foreach ($keys as $key) {
            // get an array with all values for this key if possible
            $values = \array_map(static fn ($v) => $v[$key], $this->result);
            // store the label in the labelsCache property
            $this->labelsCache[$key] = $export->getLabels($key, $values, $this->exportData);
        }
    }

    /**
     * add the headers to the csv file.
     *
     * @param resource $output
     */
    protected function prepareHeaders($output)
    {
        $keys = $this->exportManager->getExport($this->exportAlias)->getQueryKeys($this->exportData);
        // we want to keep the order of the first row. So we will iterate on the first row of the results
        $first_row = \count($this->result) > 0 ? $this->result[0] : [];
        $header_line = [];

        if (true === $this->formatterData['numerotation']) {
            $header_line[] = $this->translator->trans('Number');
        }

        foreach ($first_row as $key => $value) {
            $header_line[] = $this->translator->trans(
                $this->getLabel($key, '_header')
            );
        }

        if (\count($header_line) > 0) {
            fputcsv($output, $header_line);
        }
    }
}
