<?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;

/**
 * Create a CSV List for the export where the header are printed on the
 * first column, and the result goes from left to right.
 */
class CSVPivotedListFormatter 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',
            'data' => true,
        ]);
    }

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

    public function getName()
    {
        return 'CSV horizontal 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');

        $i = 1;
        $lines = [];
        $this->prepareHeaders($lines);

        foreach ($result as $row) {
            $j = 0;

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

            foreach ($row as $key => $value) {
                $lines[$j][] = $this->getLabel($key, $value);
                ++$j;
            }
            ++$i;
        }

        // adding the lines to the csv output
        foreach ($lines as $line) {
            fputcsv($output, $line);
        }

        $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();
        }

        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 lines array.
     *
     * @param array $lines the lines where the header will be added
     */
    protected function prepareHeaders(array &$lines)
    {
        $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']) {
            $lines[] = [$this->translator->trans('Number')];
        }

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