<?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\CustomFieldsBundle\CustomFields;

use Chill\CustomFieldsBundle\Entity\CustomField;
use Chill\MainBundle\Templating\TranslatableStringHelper;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints\GreaterThanOrEqual;
use Symfony\Component\Validator\Constraints\LessThanOrEqual;
use Twig\Environment;

/**
 * Create a custom field number.
 *
 * This number may have a min and max value, and a precision.
 */
class CustomFieldNumber extends AbstractCustomField
{
    final public const MAX = 'max';

    /**
     * key for the minimal value of the field.
     */
    final public const MIN = 'min';

    final public const POST_TEXT = 'post_text';

    final public const SCALE = 'scale';

    public function __construct(
        private readonly Environment $templating,
        private readonly TranslatableStringHelper $translatableStringHelper,
    ) {}

    public function buildForm(FormBuilderInterface $builder, CustomField $customField)
    {
        $options = $customField->getOptions();

        // select the type depending to the SCALE
        $type = (0 === $options[self::SCALE] || null === $options[self::SCALE]) ?
                IntegerType::class : NumberType::class;
        // 'integer' : 'number';

        $fieldOptions = $this->prepareFieldOptions($customField, $type);

        $builder->add($customField->getSlug(), $type, $fieldOptions);
    }

    public function buildOptionsForm(FormBuilderInterface $builder)
    {
        return $builder
            ->add(self::MIN, NumberType::class, [
                'scale' => 2,
                'label' => 'Greater or equal than',
                'required' => false,
            ])
            ->add(self::MAX, NumberType::class, [
                'scale' => 2,
                'label' => 'Lesser or equal than',
                'required' => false,
            ])
            ->add(self::SCALE, IntegerType::class, [
                'scale' => 0,
                'label' => 'Precision',
                'constraints' => [
                    new GreaterThanOrEqual(['value' => 0]),
                ],
            ])
            ->add(self::POST_TEXT, TextType::class, [
                'label' => 'Text after the field',
            ]);
    }

    public function deserialize($serialized, CustomField $customField)
    {
        return $serialized;
    }

    public function getName()
    {
        return 'Number field';
    }

    public function render($value, CustomField $customField, $documentType = 'html')
    {
        $template = '@ChillCustomFields/CustomFieldsRendering/number.'
                .$documentType.'.twig';
        $options = $customField->getOptions();

        return $this->templating
            ->render($template, [
                'number' => $value,
                'scale' => $options[self::SCALE],
                'post' => $options[self::POST_TEXT],
            ]);
    }

    public function serialize($value, CustomField $customField)
    {
        return $value;
    }

    /**
     * prepare the options'form field.
     *
     * @param string $type
     *
     * @return mixed[]
     */
    private function prepareFieldOptions(CustomField $customField, $type)
    {
        $options = $customField->getOptions();

        /**
         * @var mixed[] the formField options
         */
        $fieldOptions = [];

        // add required
        $fieldOptions['required'] = false;

        // add label
        $fieldOptions['label'] = $this->translatableStringHelper->localize($customField->getName());

        // add constraints if required
        if (null !== $options[self::MIN]) {
            $fieldOptions['constraints'][] = new GreaterThanOrEqual(['value' => $options[self::MIN]]);
        }

        if (null !== $options[self::MAX]) {
            $fieldOptions['constraints'][] = new LessThanOrEqual(['value' => $options[self::MAX]]);
        }

        // add precision to options if required
        if ('number' === $type) {
            $fieldOptions['scale'] = $options[self::SCALE];
        }

        if (!empty($options[self::POST_TEXT])) {
            $fieldOptions['post_text'] = $options[self::POST_TEXT];
        }

        return $fieldOptions;
    }
}
