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

use Psr\Log\LoggerInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Callback;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Regex;
use Symfony\Component\Validator\Context\ExecutionContextInterface;

class UserPasswordType extends AbstractType
{
    /**
     * @var LoggerInterface
     */
    protected $chillLogger;

    /**
     * @var \Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface
     */
    protected $passwordEncoder;

    public function __construct(
        \Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface $passwordEncoder,
        LoggerInterface $chillLogger,
    ) {
        $this->passwordEncoder = $passwordEncoder;
        $this->chillLogger = $chillLogger;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('new_password', RepeatedType::class, [
                'type' => PasswordType::class,
                'required' => false,
                'options' => [],
                'first_options' => [
                    'label' => 'Password',
                ],
                'second_options' => [
                    'label' => 'Repeat the password',
                ],
                'invalid_message' => 'The password fields must match',
                'constraints' => [
                    new Length([
                        'min' => 14,
                        'minMessage' => 'The password must be greater than {{ limit }} characters',
                    ]),
                    new NotBlank(),
                    new Regex([
                        'pattern' => "/((?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%!,;:+\"'-\\/{}~=µ\\(\\)£]).{6,})/",
                        'message' => 'The password must contains one letter, one '
                        .'capitalized letter, one number and one special character '
                        ."as *[@#$%!,;:+\"'-/{}~=µ()£]). Other characters are allowed.",
                    ]),
                ],
            ])
            ->add('actual_password', PasswordType::class, [
                'label' => 'Your actual password',
                'mapped' => false,
                'constraints' => [
                    new Callback([
                        'callback' => function ($password, ExecutionContextInterface $context, $payload) use ($options) {
                            if (true === $this->passwordEncoder->isPasswordValid($options['user'], $password)) {
                                return;
                            }

                            // password problem :-)
                            $this->chillLogger
                                ->notice('incorrect password when trying to change password', [
                                    'username' => $options['user']->getUsername(),
                                ]);
                            $context->addViolation('Incorrect password');
                        },
                    ]),
                ],
            ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver
            ->setRequired('user')
            ->setAllowedTypes('user', \Chill\MainBundle\Entity\User::class);
    }

    /**
     * @return string
     */
    public function getBlockPrefix()
    {
        return 'chill_mainbundle_user_password';
    }
}
