<?php

namespace apexl\Io\modules\user;

use apexl\hashing\Hash;
use apexl\Io\enums\HttpMethod;
use apexl\Io\includes\Controller;
use apexl\Io\includes\System;
use apexl\Io\interfaces\HasServicesInterface;
use apexl\Io\middleware\permission\permissionController;
use apexl\Io\middleware\session\sessionController;
use apexl\Io\modules\email\services\templateService;
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\entities\roleEntity;
use apexl\Io\modules\user\entities\userEntity;
use apexl\Io\modules\user\enums\permissions\Role;
use apexl\Io\modules\user\enums\permissions\User;
use apexl\Io\modules\user\hooks\RegisterModulePermissions;
use apexl\Io\modules\user\hooks\RegisterRolePermissions;
use apexl\Io\modules\user\hooks\SendNewUserNotification;
use apexl\Io\modules\user\interfaces\newUserNotifier;
use apexl\Io\modules\user\interfaces\registersPermissions;
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;
use app\vendor\apexl\io\src\Io\interfaces\HasHooksInterface;

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

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

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

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

    protected function addGlobalRoutes()
    {
        //@route GET /api/v{VERSION}/user/actions/logout
        $this->addActionRoute(HttpMethod::GET, 'user.logout', 'logout', userController::class.':logout');
        //@route GET /api/v{VERSION}/user/actions/login
        $this->addActionRoute(HttpMethod::POST, 'user.login', 'login', userController::class.':login');
        //@route GET /api/v{VERSION}/user/actions/login/nfc
        $this->addActionRoute(HttpMethod::POST, 'user.login.nfc', 'login/nfc', userController::class.':loginByKey');
        //@route GET /api/v{VERSION}/user/actions/forgot-password
        $this->addActionRoute(
            HttpMethod::POST,
            'user.forgot-password',
            'forgot-password',
            userController::class.':forgotPassword'
        );
        //@route GET /api/v{VERSION}/user/data/forgot-password/h/{hash}
        $this->addDataRoute(
            HttpMethod::GET,
            'user.forgot-password.hash',
            'forgot-password/h/{hash}',
            userController::class.':forgotPasswordWithLink'
        );
        $this->addActionRoute(HttpMethod::POST, 'user.refresh', 'refresh', userController::class.':refresh');
        $this->addActionRoute(
            HttpMethod::POST,
            'user.loginAsUser',
            'loginAsUser',
            userController::class.':loginAsUser',
        );
        $this->addActionRoute(
            HttpMethod::POST,
            'user.createNote',
            'create-note',
            userNoteController::class.':createUserNote',
        );
        $this->addProtectedDataRoute(
            HttpMethod::GET,
            'user-note.data.basic',
            'user-note/data/basic/{userId}',
            userNoteController::class.':userNoteListTableDataBasic',
            User::UPDATE,
        );
        $this->addActionRoute(
            HttpMethod::POST,
            'user.resetPassword',
            'reset-password',
            userController::class.':resetPassword',
            'AllowAll',
        );
    }

    /**
     * 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 permissions(Permissions $permissions): void
    {
        $permissions->registerPermission(User::VIEW, 'View Users', 'Users');
        $permissions->registerPermission(User::CREATE, 'Create Users', 'Users');
        $permissions->registerPermission(User::UPDATE, 'Update Users', 'Users');
        $permissions->registerPermission(User::UPDATE_SELF, 'Update Self', 'Users');
        $permissions->registerPermission(User::CHANGE_ACTIVE_STATE, 'Change Active State', 'Users');
        $permissions->registerPermission(User::DELETE, 'Delete Users', 'Users');
        $permissions->registerPermission(User::CAN_REGISTER, 'Can Register (For Anon Users)', 'Users');
        $permissions->registerPermission(User::MANAGE, 'Manage All User Accounts');

        $permissions->registerPermission(Role::SET, 'Can Set User Roles', 'Users');
        $permissions->registerPermission(Role::VIEW, 'View Roles', 'Roles');
        $permissions->registerPermission(Role::CREATE, 'Create Roles', 'Roles');
        $permissions->registerPermission(Role::UPDATE, 'Update Roles', 'Roles');
        $permissions->registerPermission(Role::DELETE, 'Delete Roles', 'Roles');

    }

    /**
     * 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(): array
    {
        return [
            currentUser::class => function () {
                return currentUser::getInstance();
            },
            Permissions::class => function () {
                return Permissions::getInstance();
            },
            newUserNotifier::class =>
                fn(userEntity $user) => System::makeRegisteredService(
                    newUserEmailNotifier::class,
                    ['user' => $user]
                ),

        ];
    }

    public function install(): void
    {
        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();
    }

    public function hooks(): array
    {
        return [
            RegisterRolePermissions::class,
            SendNewUserNotification::class,
            RegisterModulePermissions::class,
        ];
    }
}
