<?php

namespace apexl\Io\modules\user;

use apexl\hashing\Hash;
use apexl\Io\includes\Controller;
use apexl\Io\includes\Hook;
use apexl\Io\includes\System;
use apexl\Io\middleware\session\sessionController;
use apexl\Io\middleware\permission\permissionController;
use apexl\Io\modules\email\services\templateService;
use apexl\Io\modules\subscription\controllers\subscriptionController;
use apexl\Io\modules\system\services\modules;
use apexl\Io\modules\user\callbacks\callbacks;
use apexl\Io\modules\user\callbacks\initialiseModules;
use apexl\Io\modules\user\classes\userManagedApiModule;
use apexl\Io\modules\user\controllers\rolesController;
use apexl\Io\modules\user\controllers\userController;
use apexl\Io\modules\user\controllers\userNoteController;
use apexl\Io\modules\user\controllers\userNotificationController;
use apexl\Io\modules\user\entities\roleEntity;
use apexl\Io\modules\user\entities\userEntity;
use apexl\Io\modules\user\interfaces\newUserNotifier;
use apexl\Io\modules\user\notifiers\newUserEmailNotifier;
use apexl\Io\modules\user\services\currentUser;
use apexl\Io\modules\user\services\Permissions;
use apexl\Vault\Vault;

/**
 * Base module file, provides required methods to register things like routes and middleware.
 * Class installModule
 * @package apexl\Io\modules\install
 */
class userModule extends userManagedApiModule
{
    public function __construct()
    {
        parent::__construct();

        templateService::$locations[] = sprintf('%s/templates/email', __DIR__);
    }

    public function routes()
    {
        $this->addGlobalRoutes();
        //@route VERB /api/v{VERSION}/user/user
        $this->addProtectedEntityRoutes((new userEntity()), [
            'get' => [userController::class.':userListTableData', 'ViewUsers'],
            'put' => [userController::class.':register', 'UpdateSelf'],
            'post' => [userController::class.':register', 'UpdateSelf'],
            'delete' => [Controller::class.':delete', 'DeleteUsers'],
        ]);

        //@route VERB /api/v{VERSION}/user/role
        $this->addProtectedEntityRoutes((new roleEntity()), [
            'get' => [rolesController::class.':getRoles', 'ViewRoles'],
            'put' => [rolesController::class.':createUpdate', 'CreateRoles'],
            'post' => [rolesController::class.':createUpdate', 'CreateRoles'],
            'delete' => [rolesController::class.':delete', 'DeleteRoles'],
        ]);
    }

    protected function addGlobalRoutes()
    {
        //@route GET /api/v{VERSION}/user/actions/logout
        $this->addActionRoute('GET', 'user.logout', 'logout', userController::class.':logout');
        //@route GET /api/v{VERSION}/user/actions/login
        $this->addActionRoute('POST', 'user.login', 'login', userController::class.':login');
        //@route GET /api/v{VERSION}/user/actions/forgot-password
        $this->addActionRoute('POST', 'user.forgot-password', 'forgot-password', userController::class.':forgotPassword');
        //@route GET /api/v{VERSION}/user/data/forgot-password/h/{hash}
        $this->addDataRoute('GET', 'user.forgot-password.hash', 'forgot-password/h/{hash}', userController::class.':forgotPasswordWithLink');
        $this->addActionRoute('POST', 'user.refresh', 'refresh', userController::class.':refresh');
        $this->addActionRoute('POST', 'user.loginAsUser', 'loginAsUser',userController::class.':loginAsUser', 'ManageUsers');
        $this->addActionRoute('POST', 'user.createNote', 'create-note',userNoteController::class.':createUserNote', 'UpdateUsers');
        $this->addActionRoute('POST', 'user.resetPassword', 'reset-password',userController::class.':resetPassword', 'AllowAll');
        $this->addActionRoute('POST', 'user.updateSettings', 'update-settings',userController::class.':updateSettings', 'UpdateSelf');
        $this->addActionRoute('POST', 'user.markAllAsRead', 'notifications/markAllAsRead',userNotificationController::class.':markAllAsRead', 'UpdateSelf');
        $this->addProtectedDataRoute('get', 'user-note.data.basic', 'user-note/data/basic/{userId}', userNoteController::class.':userNoteListTableDataBasic', 'UpdateUsers');
        $this->addProtectedActionRoute('post', 'user.suspend', 'suspend-now/{id}', userController::class.':suspendUserNow', 'ManageUsers');
        $this->addProtectedActionRoute('post', 'user.validate', 'validate-now/{id}', userController::class.':validateUserNow', 'ManageUsers');
    }

    /**
     * Function to add the middleware required by this module.
     * NOTE - Slim v4 is LIFO so the middleware added last is loaded first.
     */
    public function addMiddleware()
    {
        $this->Io->add(permissionController::class);
        $this->Io->add(sessionController::class);
    }

    public function registerPermissions(Permissions $permissions)
    {
        $permissions->registerPermission('ViewUsers', 'View Users', 'Users');
        $permissions->registerPermission('CreateUsers', 'Create Users', 'Users');
        $permissions->registerPermission('UpdateUsers', 'Update Users', 'Users');
        $permissions->registerPermission('UpdateSelf', 'Update Self', 'Users');
        $permissions->registerPermission('ChangeUserActiveState', 'Change Active State', 'Users');
        $permissions->registerPermission('DeleteUsers', 'Delete Users', 'Users');
        $permissions->registerPermission('CanRegister', 'Can Register (For Anon Users)', 'Users');
        $permissions->registerPermission('CanSetUserRole', 'Can Set User Roles', 'Users');

        $permissions->registerPermission('ViewRoles', 'View Roles', 'Roles');
        $permissions->registerPermission('CreateRoles', 'Create Roles', 'Roles');
        $permissions->registerPermission('UpdateRoles', 'Update Roles', 'Roles');
        $permissions->registerPermission('DeleteRoles', 'Delete Roles', 'Roles');

        $permissions->registerPermission('ManageUsers', 'Manage All User Accounts');
    }

    /**
     * Function to return services that are added to the PHP DI container and can be lazy loaded to all Slim routes and/or middleware
     * @return array
     */
    public function addServices()
    {
        return [
            currentUser::class => function () {
                return currentUser::getInstance();
            },
            Permissions::class => function () {
                return Permissions::getInstance();
            },
            newUserNotifier::class =>
                fn (userEntity $user) => System::makeRegisteredService(
                    newUserEmailNotifier::class,
                    ['user' => $user]
                ),

        ];
    }

    /**
     * Register callbacks.
     * @see modules:initialiseActiveModuleServices();
     */
    public function registerCallbacks()
    {
        Hook::registerCallback('postAuth', callbacks::class.':postAuth');
        Hook::registerCallback('initialiseModule', initialiseModules::class.':processPermissions');
        Hook::registerCallback('userPostSave', callbacks::class.':sendNewUserNotification');
        Hook::registerCallback('addMessage', callbacks::class.':storeMessage');
    }

    public function schema()
    {
        return System::loadSchema(__DIR__.'/schema/schema.json');
    }

    public function install()
    {
        if (!$this->database) {
            $this->database =  Vault::getInstance();
        }
        //we need to set a few defaults, including the anon user and user 1.
        $this->database->insert('users')->fields(
            [
                'first_name' => "Anonymous",
                'last_name' => "user",
                'active' => 1,
                'email' => 'noemail@anon',
                'password' => 'ThisIsNotAPassword',
                'created' => time(),
                'created_by' => 0,
                'salt' => 'ThisIsNotASalt'
            ]
        )->execute();
        //next we have to update the auto incrementing id field to 0.
        $this->database->update('users')->fields(['id' => 0])->where('email', 'noemail@anon')->execute();

        //@todo this should come from config of the create URL.
        //user 1, for now we set defaults.
        $hash = new Hash();
        $hashData = $hash->hashString("8aMP4WgjGvN3eZJQ");
        $passwordHash = $hashData->hash;
        $passwordSalt = $hashData->salt;

        $this->database->insert('users')->fields([
            'id' => 1,
            'first_name' => "admin",
            'last_name' => "user",
            'active' => 1,
            'email' => 'admin@apexlstudios.com',
            'password' => $passwordHash,
            'created' => time(),
            'created_by' => 1,
            'salt' => $passwordSalt
        ])->execute();

        //create a default admin role
        $this->database->insert('roles')->fields(
            [
                'name' => "Anonymous",
                'permissions' => serialize([
                    'CanRegister',
                    'UpdateSelf'
                ]),
                'created' => time(),
                'created_by' => 1,
                'modified' => time(),
                'modified_by' => 1
            ]
        )->execute();

        $this->database->insert('roles')->fields(
            [
                'name' => "Administrator",
                'permissions' => serialize([
                    'ViewUsers',
                    'CreateUsers',
                    'UpdateUsers',
                    'ChangeUserActiveState',
                    'DeleteUsers',
                    'CanRegister',
                    'CanSetUserRole',
                    'ViewRoles',
                    'CreateRoles',
                    'UpdateRoles',
                    'DeleteRoles'
                ]),
                'created' => time(),
                'created_by' => 1,
                'modified' => time(),
                'modified_by' => 1
            ]
        )->execute();
    }
}
