<?php
declare(strict_types=1);

/**
 * BEdita, API-first content management framework
 * Copyright 2017 ChannelWeb Srl, Chialab Srl
 *
 * This file is part of BEdita: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * See LICENSE.LGPL or <http://gnu.org/licenses/lgpl-3.0.html> for more details.
 */

namespace BEdita\Core\Model\Action;

use BEdita\Core\Mailer\UserMailerTrait;
use BEdita\Core\Model\Entity\User;
use Cake\Event\EventDispatcherTrait;
use Cake\Event\EventInterface;
use Cake\Event\EventListenerInterface;
use Cake\Http\Exception\BadRequestException;
use Cake\Http\Exception\ConflictException;
use Cake\I18n\FrozenTime;
use Cake\ORM\TableRegistry;

/**
 * Command for activate a user after signup
 *
 * @since 4.0.0
 */
class SignupUserActivationAction extends BaseAction implements EventListenerInterface
{
    use EventDispatcherTrait;
    use UserMailerTrait;

    /**
     * The UsersTable table
     *
     * @var \BEdita\Core\Model\Table\UsersTable
     */
    protected $Users;

    /**
     * The AsyncJobs table
     *
     * @var \BEdita\Core\Model\Table\AsyncJobsTable
     */
    protected $AsyncJobs;

    /**
     * @inheritDoc
     */
    protected function initialize(array $config)
    {
        $this->Users = TableRegistry::getTableLocator()->get('Users');
        $this->AsyncJobs = TableRegistry::getTableLocator()->get('AsyncJobs');

        $this->getEventManager()->on($this);
    }

    /**
     * {@inheritDoc}
     *
     * @throws \Cake\Http\Exception\BadRequestException When missing id or async_jobs row is invalid
     * @throws \Cake\Http\Exception\ConflictException When the user is already active
     */
    public function execute(array $data = [])
    {
        if (empty($data['uuid'])) {
            throw new BadRequestException(__d('bedita', 'Parameter "{0}" missing', ['uuid']));
        }

        $asyncJob = $this->AsyncJobs->get($data['uuid'], ['finder' => 'incomplete']);

        if (empty($asyncJob->payload['user_id'])) {
            throw new BadRequestException(__d('bedita', 'Invalid async job, missing user_id'));
        }

        $user = $this->Users->get($asyncJob->payload['user_id'], ['contain' => ['Roles']]);
        if ($user->status === 'on' && $user->verified !== null) {
            throw new ConflictException(__d('bedita', 'User already active'));
        }

        $now = new FrozenTime();

        // the user is the creator of himself
        $user->created_by = $user->id;
        $user->modified_by = $user->id;
        $user->status = 'on';
        $user->verified = $now;
        $this->Users->saveOrFail($user);

        $asyncJob->completed = $now;
        $this->AsyncJobs->save($asyncJob);

        $this->dispatchEvent('Auth.signupActivation', [$user, $asyncJob], $this->Users);

        return $user;
    }

    /**
     * Send welcome email to user to inform him of successfully activation
     *
     * @param \Cake\Event\EventInterface $event Dispatched event.
     * @param \BEdita\Core\Model\Entity\User $user The user
     * @return void
     */
    public function sendMail(EventInterface $event, User $user)
    {
        $options = [
            'params' => compact('user'),
        ];
        $this->getUserMailer()->send('welcome', [$options]);
    }

    /**
     * @inheritDoc
     */
    public function implementedEvents(): array
    {
        return [
            'Auth.signupActivation' => 'sendMail',
        ];
    }
}
