<?php

/*
 * This file is part of RaspiSMS.
 *
 * (c) Pierre-Lin Bonnemaison <plebwebsas@gmail.com>
 *
 * This source file is subject to the GPL-3.0 license that is bundled
 * with this source code in the file LICENSE.
 */

namespace controllers\publics;

/**
 * Page des users.
 */
class User extends \descartes\Controller
{
    private $internal_user;
    private $internal_quota;
    private $internal_setting;

    /**
     * Cette fonction est appelée avant toute les autres :
     * Elle vérifie que l'utilisateur est bien connecté.
     *
     * @return void;
     */
    public function __construct()
    {
        $bdd = \descartes\Model::_connect(DATABASE_HOST, DATABASE_NAME, DATABASE_USER, DATABASE_PASSWORD);
        $this->internal_user = new \controllers\internals\User($bdd);
        $this->internal_quota = new \controllers\internals\Quota($bdd);
        $this->internal_setting = new \controllers\internals\Setting($bdd);

        \controllers\internals\Tool::verifyconnect();

        if (!\controllers\internals\Tool::is_admin())
        {
            return $this->redirect(\descartes\Router::url('Dashboard', 'show'));
        }
    }

    /**
     * Cette fonction retourne tous les users, sous forme d'un tableau permettant l'administration de ces users.
     */
    public function list()
    {
        $this->render('user/list');
    }

    /**
     * Return users as json.
     */
    public function list_json()
    {
        $entities = $this->internal_user->list();

        foreach ($entities as &$entity)
        {
            $quota_percentage = $this->internal_quota->get_usage_percentage($entity['id']);
            $entity['quota_percentage'] = $quota_percentage * 100;

            $quota = $this->internal_quota->get_user_quota($entity['id']);
            if (!$quota)
            {
                continue;
            }

            if (new \DateTime() > new \DateTime($quota['expiration_date']))
            {
                $entity['quota_expired_at'] = $quota['expiration_date'];
            }
        }

        header('Content-Type: application/json');
        echo json_encode(['data' => $entities]);
    }

    /**
     * Update status of users.
     *
     * @param array int $_GET['user_ids'] : User ids
     * @param mixed     $csrf
     * @param int       $status           : 1 -> active, 0 -> suspended
     *
     * @return boolean;
     */
    public function update_status($csrf, int $status)
    {
        if (!$this->verify_csrf($csrf))
        {
            \FlashMessage\FlashMessage::push('danger', 'Jeton CSRF invalid !');

            return $this->redirect(\descartes\Router::url('User', 'list'));
        }

        if (0 === $status)
        {
            $status = \models\User::STATUS_SUSPENDED;
        }
        else
        {
            $status = \models\User::STATUS_ACTIVE;
        }

        $ids = $_GET['user_ids'] ?? [];
        foreach ($ids as $id)
        {
            $this->internal_user->update_status($id, $status);
        }

        return $this->redirect(\descartes\Router::url('User', 'list'));
    }

    /**
     * Cette fonction va supprimer une liste de users.
     *
     * @param array int $_GET['user_ids'] : Les id des useres à supprimer
     * @param mixed     $csrf
     *
     * @return boolean;
     */
    public function delete($csrf)
    {
        if (!$this->verify_csrf($csrf))
        {
            \FlashMessage\FlashMessage::push('danger', 'Jeton CSRF invalid !');

            return $this->redirect(\descartes\Router::url('User', 'list'));
        }

        if (!\controllers\internals\Tool::is_admin())
        {
            \FlashMessage\FlashMessage::push('danger', 'Vous devez être administrateur pour supprimer un utilisateur !');

            return $this->redirect(\descartes\Router::url('User', 'list'));
        }

        $ids = $_GET['user_ids'] ?? [];
        foreach ($ids as $id)
        {
            $this->internal_user->delete($id);
        }

        return $this->redirect(\descartes\Router::url('User', 'list'));
    }

    /**
     * Cette fonction retourne la page d'ajout d'un user.
     */
    public function add()
    {
        $now = new \DateTime();
        $now = $now->format('Y-m-d H:i:00');

        return $this->render('user/add', ['now' => $now]);
    }

    /**
     * Cette fonction insert un nouveau user.
     *
     * @param $csrf : Le jeton CSRF
     * @param string           $_POST['email']                          : User email
     * @param optional string  $_POST['password']                       : User password, (if empty the password is randomly generated)
     * @param optional boolean $_POST['admin']                          : If true user is admin
     * @param optional boolean $_POST['quota_enable']                   : If true create a quota for the user
     * @param bool             $_POST['quota_enable']                   : If true create a quota for the user
     * @param optional int     $_POST['quota_credit']                   : credit for quota
     * @param optional int     $_POST['quota_additional']               : additional credit
     * @param optional string  $_POST['quota_start_date']               : quota beginning date
     * @param optional string  $_POST['quota_renewal_interval']         : period to use on renewal to calculate new expiration date. Also use to calculate first expiration date.
     * @param optional boolean $_POST['quota_auto_renew']               : Should the quota be automatically renewed on expiration
     * @param optional boolean $_POST['quota_report_unused']            : Should unused credit be reported next month
     * @param optional boolean $_POST['quota_report_unused_additional'] : Should unused additional credit be transfered next month
     */
    public function create($csrf)
    {
        if (!$this->verify_csrf($csrf))
        {
            \FlashMessage\FlashMessage::push('danger', 'Jeton CSRF invalid !');

            return $this->redirect(\descartes\Router::url('User', 'add'));
        }

        $email = $_POST['email'] ?? false;
        $password = !empty($_POST['password']) ? $_POST['password'] : \controllers\internals\Tool::generate_password(rand(6, 12));
        $admin = $_POST['admin'] ?? false;
        $status = 'active';
        $quota_enable = $_POST['quota_enable'] ?? false;
        $quota_credit = $_POST['quota_credit'] ?? false;
        $quota_additional = $_POST['quota_additional'] ?? false;
        $quota_start_date = $_POST['quota_start_date'] ?? false;
        $quota_renew_interval = $_POST['quota_renew_interval'] ?? false;
        $quota_auto_renew = $_POST['quota_auto_renew'] ?? false;
        $quota_report_unused = $_POST['quota_report_unused'] ?? false;
        $quota_report_unused_additional = $_POST['quota_report_unused_additional'] ?? false;

        if (!$email)
        {
            \FlashMessage\FlashMessage::push('danger', 'Vous devez au moins fournir une adresse e-mail pour l\'utilisateur.');

            return $this->redirect(\descartes\Router::url('User', 'add'));
        }

        if (!filter_var($email, FILTER_VALIDATE_EMAIL))
        {
            \FlashMessage\FlashMessage::push('danger', 'L\'adresse e-mail n\'est pas valide.');

            return $this->redirect(\descartes\Router::url('User', 'add'));
        }

        //Forge quota for user if needed
        $quota = null;
        if ($quota_enable)
        {
            $quota = [];
            $quota['credit'] = (int) $quota_credit;
            $quota['additional'] = (int) $quota_additional;

            if (false === $quota_start_date || !\controllers\internals\Tool::validate_date($quota_start_date, 'Y-m-d H:i:s'))
            {
                \FlashMessage\FlashMessage::push('danger', 'Vous devez définir une date de début valide pour le quota.');

                return $this->redirect(\descartes\Router::url('User', 'add'));
            }
            $quota['start_date'] = new \DateTime($quota_start_date);

            if (false === $quota_renew_interval || !\controllers\internals\Tool::validate_period($quota_renew_interval))
            {
                \FlashMessage\FlashMessage::push('danger', 'Vous devez définir une durée de quota parmis la liste proposée.');

                return $this->redirect(\descartes\Router::url('User', 'add'));
            }
            $quota['renew_interval'] = $quota_renew_interval;

            $quota['expiration_date'] = clone $quota['start_date'];
            $quota['expiration_date']->add(new \DateInterval($quota_renew_interval));

            $quota['auto_renew'] = (bool) $quota_auto_renew;
            $quota['report_unused'] = (bool) $quota_report_unused;
            $quota['report_unused_additional'] = (bool) $quota_report_unused_additional;
        }

        $id_user = $this->internal_user->create($email, $password, $admin, null, \models\User::STATUS_ACTIVE, true, $quota);
        if (!$id_user)
        {
            \FlashMessage\FlashMessage::push('danger', 'Impossible de créer cet utilisateur.');

            return $this->redirect(\descartes\Router::url('User', 'add'));
        }

        $mailer = new \controllers\internals\Mailer();
        $email_send = $mailer->enqueue($email, EMAIL_CREATE_USER, ['email' => $email, 'password' => $password]);
        if (!$email_send)
        {
            \FlashMessage\FlashMessage::push('danger', 'Impossible d\'envoyer l\'e-mail à l\'utilisateur.');
        }

        \FlashMessage\FlashMessage::push('success', 'L\'utilisateur a bien été créé.');

        return $this->redirect(\descartes\Router::url('User', 'list'));
    }

    /**
     * Return the edition page for the users.
     *
     * @param int... $ids : users ids
     */
    public function edit()
    {
        $ids = $_GET['user_ids'] ?? [];
        $id_user = $_SESSION['user']['id'];

        $users = $this->internal_user->gets_in_by_id($ids);

        if (!$users)
        {
            return $this->redirect(\descartes\Router::url('User', 'list'));
        }

        foreach ($users as &$user)
        {
            $user['quota'] = $this->internal_quota->get_user_quota($user['id']);
        }

        $now = new \DateTime();
        $now = $now->format('Y-m-d H:i:00');

        $this->render('user/edit', [
            'users' => $users,
            'now' => $now,
        ]);
    }

    /**
     * Update a list of users.
     *
     * @param $csrf : Le jeton CSRF
     * @param array $_POST['users'] : Array of the users and new values, id as key. Quota may also be defined.
     */
    public function update($csrf)
    {
        if (!$this->verify_csrf($csrf))
        {
            \FlashMessage\FlashMessage::push('danger', 'Jeton CSRF invalid !');

            return $this->redirect(\descartes\Router::url('User', 'add'));
        }

        $nb_update = 0;
        $users = $_POST['users'] ?? [];
        foreach ($users as $id_user => $user)
        {
            $email = $user['email'] ?? false;
            $password = !empty($user['password']) ? $user['password'] : null;
            $admin = $user['admin'] ?? false;

            $quota_enable = $user['quota_enable'] ?? false;
            $quota_consumed = $user['quota_consumed'] ?? false;
            $quota_credit = $user['quota_credit'] ?? false;
            $quota_additional = $user['quota_additional'] ?? false;
            $quota_start_date = $user['quota_start_date'] ?? false;
            $quota_renew_interval = $user['quota_renew_interval'] ?? false;
            $quota_auto_renew = $user['quota_auto_renew'] ?? false;
            $quota_report_unused = $user['quota_report_unused'] ?? false;
            $quota_report_unused_additional = $user['quota_report_unused_additional'] ?? false;

            if (!$email)
            {
                \FlashMessage\FlashMessage::push('danger', 'L\'utilisateur #' . (int) $id_user . ' n\'as pas pu être mis à jour car l\'adresse e-mail n\'as pas été fournie.');

                continue;
            }

            if (!filter_var($email, FILTER_VALIDATE_EMAIL))
            {
                \FlashMessage\FlashMessage::push('danger', 'L\'utilisateur #' . (int) $id_user . ' n\'as pas pu être mis à jour car l\'adresse e-mail fournie n\'est pas valide.');

                return $this->redirect(\descartes\Router::url('User', 'add'));
            }

            //Forge quota for user if needed
            $quota = false;
            if ($quota_enable)
            {
                $quota = [];
                $quota['credit'] = (int) $quota_credit;
                $quota['consumed'] = (int) $quota_consumed;
                $quota['additional'] = (int) $quota_additional;

                if (false === $quota_start_date || !\controllers\internals\Tool::validate_date($quota_start_date, 'Y-m-d H:i:s'))
                {
                    \FlashMessage\FlashMessage::push('danger', 'L\'utilisateur #' . (int) $id_user . ' n\'as pas pu être mis à jour car la date de début du quota associé n\'est pas valide.');

                    continue;
                }
                $quota['start_date'] = new \DateTime($quota_start_date);

                if (false === $quota_renew_interval || !\controllers\internals\Tool::validate_period($quota_renew_interval))
                {
                    \FlashMessage\FlashMessage::push('danger', 'L\'utilisateur #' . (int) $id_user . ' n\'as pas pu être mis à jour car la durée du quota associé n\'est pas valide.');

                    continue;
                }
                $quota['renew_interval'] = $quota_renew_interval;

                $quota['expiration_date'] = clone $quota['start_date'];
                $quota['expiration_date']->add(new \DateInterval($quota_renew_interval));

                $quota['auto_renew'] = (bool) $quota_auto_renew;
                $quota['report_unused'] = (bool) $quota_report_unused;
                $quota['report_unused_additional'] = (bool) $quota_report_unused_additional;

                //Format dates
                $quota['start_date'] = $quota['start_date']->format('Y-m-d H:i:s');
                $quota['expiration_date'] = $quota['expiration_date']->format('Y-m-d H:i:s');
            }

            $updated_user = [
                'email' => $email,
                'admin' => $admin,
            ];

            if ($password)
            {
                $updated_user['password'] = password_hash($password, PASSWORD_DEFAULT);
            }

            $success = $this->internal_user->update($id_user, $updated_user, $quota);
            if (!$success)
            {
                \FlashMessage\FlashMessage::push('danger', 'L\'utilisateur #' . (int) $id_user . ' n\'as pas pu être mis à jour.');

                continue;
            }

            ++$nb_update;
        }

        if ($nb_update != count($users))
        {
            \FlashMessage\FlashMessage::push('danger', 'Certains utilisateurs n\'ont pas pu être mis à jour.');

            return $this->redirect(\descartes\Router::url('User', 'list'));
        }

        \FlashMessage\FlashMessage::push('success', 'Tous les utilisateurs ont bien été mis à jour.');

        return $this->redirect(\descartes\Router::url('User', 'list'));
    }

    /**
     * Allow an admin to impersonate a user.
     *
     * @param mixed     $csrf
     * @param array int $_GET['user_ids'] : Ids of users to impersonate, the array should actually contain one id only, we keep use of array for simpler compatibility in UI
     */
    public function impersonate($csrf)
    {
        if (!$this->verify_csrf($csrf))
        {
            \FlashMessage\FlashMessage::push('danger', 'Jeton CSRF invalid !');

            return $this->redirect(\descartes\Router::url('User', 'list'));
        }

        if (1 != count($_GET['user_ids']))
        {
            \FlashMessage\FlashMessage::push('danger', 'Vous devez séléctionner un et un seul utilisateur à incarner !');

            return $this->redirect(\descartes\Router::url('User', 'list'));
        }

        $id_user = (int) $_GET['user_ids'][0];

        //Check if this user exists
        $user = $this->internal_user->get($id_user);
        if (!$user)
        {
            \FlashMessage\FlashMessage::push('danger', 'Cet utilisateur n\'existe pas !');

            return $this->redirect(\descartes\Router::url('User', 'list'));
        }

        $settings = $this->internal_setting->gets_for_user($id_user);
        if (!$settings)
        {
            \FlashMessage\FlashMessage::push('danger', 'Impossible de charger les settings de cet utilisateur !');

            return $this->redirect(\descartes\Router::url('User', 'list'));
        }

        if (\models\User::STATUS_ACTIVE !== $user['status'])
        {
            \FlashMessage\FlashMessage::push('danger', 'Impossible d\'incarner cet utilisateur car il est actuellement suspendu');

            return $this->redirect(\descartes\Router::url('User', 'list'));
        }

        $user['settings'] = $settings;

        //Save old session to get it back later
        $old_session = $_SESSION;
        $_SESSION = [
            'old_session' => $old_session,
            'impersonate' => true,
            'connect' => true,
            'user' => $user,
        ];

        \FlashMessage\FlashMessage::push('success', 'Vous incarnez désormais l\'utilisateur ' . $user['email'] . '.');

        return $this->redirect(\descartes\Router::url('Dashboard', 'show'));
    }
}
