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

use Doctrine\ORM\NativeQuery;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Contracts\Translation\TranslatableInterface;

/**
 * Interface for Export.
 *
 * An export is a class which will initiate a query for an export.
 *
 * **Note** : the report implementing this class will be modified by
 * both filters **and** aggregators. If the report does not support
 * aggregation, use `ListInterface`.
 *
 * @example Chill\PersonBundle\Export\CountPerson an example of implementation
 *
 * @template Q of QueryBuilder|NativeQuery
 * @template D of array
 */
interface ExportInterface extends ExportElementInterface
{
    /**
     * Add a form to collect data from the user.
     */
    public function buildForm(FormBuilderInterface $builder);

    /**
     * Get the default data, that can be use as "data" for the form.
     */
    public function getFormDefaultData(): array;

    /**
     * Return which formatter type is allowed for this report.
     *
     * @return string[]
     */
    public function getAllowedFormattersTypes();

    /**
     * A description, which will be used in the UI to explain what the export does.
     * This description will be translated.
     *
     * @return string
     */
    public function getDescription();

    /**
     * transform the results to viewable and understable string.
     *
     * The callable will have only one argument: the `value` to translate.
     *
     * The callable should also be able to return a key `_header`, which
     * will contains the header of the column.
     *
     * The string returned **must** be already translated if necessary,
     * **with an exception** for the string returned for `_header`.
     *
     * Example :
     *
     * ```
     * protected $translator;
     *
     * public function getLabels($key, array $values, $data)
     * {
     *      return function($value) {
     *          case $value
     *          {
     *              case '_header' :
     *                  return 'my header not translated';
     *              case true:
     *                  return $this->translator->trans('true');
     *              case false:
     *                  return $this->translator->trans('false');
     *              default:
     *                  // this should not happens !
     *                  throw new \LogicException();
     *          }
     * }
     * ```
     *
     * **Note:** Why each string must be translated with an exception for
     * the `_header` ? For performance reasons: most of the value will be number
     * which do not need to be translated, or value already translated in
     * database. But the header must be, in every case, translated.
     *
     * @param string      $key    The column key, as added in the query
     * @param list<mixed> $values The values from the result. if there are duplicates, those might be given twice. Example: array('FR', 'BE', 'CZ', 'FR', 'BE', 'FR')
     *
     * @return (callable(string|int|float|'_header'|null $value): string|int|\DateTimeInterface|TranslatableInterface) where the first argument is the value, and the function should return the label to show in the formatted file. Example : `function($countryCode) use ($countries) { return $countries[$countryCode]->getName(); }`
     */
    public function getLabels(string $key, array $values, mixed $data);

    /**
     * give the list of keys the current export added to the queryBuilder in
     * self::initiateQuery.
     *
     * Example: if your query builder will contains `SELECT count(id) AS count_id ...`,
     * this function will return `array('count_id')`.
     *
     * @param D $data the data from the export's form (added by self::buildForm)
     */
    public function getQueryKeys(array $data): array;

    /**
     * Return the results of the query builder.
     *
     * @param Q $query
     * @param D $data  the data from the export's fomr (added by self::buildForm)
     *
     * @return mixed[] an array of results
     */
    public function getResult(QueryBuilder|NativeQuery $query, array $data, ExportGenerationContext $context): array;

    /**
     * Return the Export's type. This will inform _on what_ export will apply.
     * Most of the type, it will be a string which references an entity.
     *
     * Example of types : Chill\PersonBundle\Export\Declarations::PERSON_TYPE
     */
    public function getType(): string;

    /**
     * The initial query, which will be modified by ModifiersInterface
     * (i.e. AggregatorInterface, FilterInterface).
     *
     * This query should take into account the `$acl` and restrict result only to
     * what the user is allowed to see. (Do not show personal data the user
     * is not allowed to see).
     *
     * The returned object should be an instance of QueryBuilder or NativeQuery.
     *
     * @param array $acl  an array where each row has a `center` key containing the Chill\MainBundle\Entity\Center, and `circles` keys containing the reachable circles. Example: `array( array('center' => $centerA, 'circles' => array($circleA, $circleB) ) )`
     * @param array $data the data from the form, if any
     *
     * @return Q the query to execute
     */
    public function initiateQuery(array $requiredModifiers, array $acl, array $data, ExportGenerationContext $context): QueryBuilder|NativeQuery;

    /**
     * @param D $formData
     */
    public function normalizeFormData(array $formData): array;

    /**
     * @param array $formData the normalized data
     *
     * @return D
     */
    public function denormalizeFormData(array $formData, int $fromVersion): array;

    public function getNormalizationVersion(): int;

    /**
     * Return the required Role to execute the Export.
     */
    public function requiredRole(): string;

    /**
     * Inform which ModifiersInterface (i.e. AggregatorInterface, FilterInterface)
     * are allowed. The modifiers should be an array of types the _modifier_ apply on
     * (@see ModifiersInterface::applyOn()).
     *
     * @return string[]
     */
    public function supportsModifiers();
}
