<?php

namespace apexl\Io\modules\userDisplay\controllers;

use apexl\Io\includes\Controller;
use apexl\Io\includes\Hook;
use apexl\Io\includes\Routes;
use apexl\Io\includes\System;
use apexl\Io\modules\display\components\BasicLink;
use apexl\Io\modules\display\components\ButtonBar;
use apexl\Io\modules\display\components\ContentTitle;
use apexl\Io\modules\display\components\EntityFilteredDisplayTable;
use apexl\Io\modules\display\components\FormComponent;
use apexl\Io\modules\display\components\genericComponents;
use apexl\Io\modules\display\components\RowWrapper;
use apexl\Io\modules\display\components\SimpleButton;
use apexl\Io\modules\display\services\Render;
use apexl\Io\modules\formbuilder\entities\formEntity;
use apexl\Io\modules\formbuilder\includes\checkboxField;
use apexl\Io\modules\formbuilder\includes\inputField;
use apexl\Io\modules\userDisplay\callbacks\tableRowAlters;
use apexl\Io\modules\userDisplay\components\dashboardTiles;
use apexl\Io\modules\userDisplay\components\roleEntityDisplayTile;
use apexl\Io\modules\userDisplay\components\userEntityDisplayTile;
use apexl\Io\modules\userDisplay\forms\forgottenPasswordForm;
use apexl\Io\modules\userDisplay\forms\loginForm;
use apexl\Io\modules\user\entities\roleEntity;
use apexl\Io\modules\user\entities\userEntity;
use apexl\Io\modules\user\services\currentUser;
use apexl\Io\services\Output;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

class userController extends Controller
{
    protected $currentUser;
    protected $render;

    public function __construct(Render $render, currentUser $currentUser)
    {
        parent::__construct();
        $this->currentUser = $currentUser::getCurrentUser();
        $this->render = $render;
    }

    /**
     * @param Request $request
     * @param Response $response
     * @param $args
     * @return Response
     * @throws \Exception
     */
    public function userViewSingle(Request $request, Response $response, $args)
    {
        $rowWrapper = (new RowWrapper())->addClass('manageForm-wrapper');
        $rowWrapper->addComponent(dashboardTiles::userViewTile($args['id']));

        $userEntity = new userEntity();
        $userEntity->load($args['id']);

        $buttonsBar = new ButtonBar();
        $buttonsBar->setEntity($userEntity);

        $this->render::setPageTitle('Viewing ['.$userEntity->id.'] - '.$userEntity->getNiceName());

        return System::asJson($response, $this->render::build([$buttonsBar, $rowWrapper]));
    }

    /**
     * @param Request $request
     * @param Response $response
     * @return Response
     */
    public function userSettingsPage(Request $request, Response $response)
    {
        $form = (new FormComponent())
            ->src(Routes::getRoutePattern('user.display.forms.userSettings'))
            ->setID('register');

        $this->render::setPageTitle('User Settings');

        return System::asJson($response, $this->render::build([$form]));
    }

    /**
     * @param Request $request
     * @param Response $response
     * @return Response
     * @throws \Exception
     */
    public function userSettingsForm(Request $request, Response $response)
    {
        return $this->userEditForm($request, $response, ['id' => $this->currentUser->id]);
    }

    /**
     * Login page - @see userDisplayController in io-user
     * @param Request $request
     * @param Response $response
     * @return Response
     */
    public function loginPage(Request $request, Response $response, $args)
    {
        if (currentUser::getCurrentUser()->isLoggedIn()) {
            Output::addMetadata('user.login.redirect', 'redirect', '/');
            return System::asJson($response);
        }

        $this->render::setActive("noUI");
        $this->render::setMetaTitle("Login | ". ($this->config->app->defaultMetaTitle ?? "Io Dashboard"));
        $components = [];
        if (isset($args['loginMessageType']) && isset($this->config->app->loginMessageTypes->{$args['loginMessageType']})) {
            $components[] = (new ContentTitle())->addContent($this->config->app->loginMessageTypes->{$args['loginMessageType']})->addClass('text-light');
        }
        $components[] = (new FormComponent())->src(Routes::getRoutePattern('user.display.forms.login'))
            ->title(System::getVariable('user_login_description_text') ?? "Sign in with your email address.")
            ->addClass('login-form')->setID('login');
        $components[] = (new BasicLink())->addClass('forgottenPasswordLink')->addRoute(Routes::getRoutePattern('user.display.forgot-password'))->addText(System::getVariable('user_login_forgot_password_text') ?? 'Forgot password?');
        $components[] = (new BasicLink())->addClass('registerLink')->addRoute(Routes::getRoutePattern('user.display.register'))->addText(System::getVariable('user_login_register_text') ?? 'No account? Register.');
        return System::asJson($response, $this->render::build($components));
    }

    public function registerPage(Request $request, Response $response, $args)
    {
        if (currentUser::getCurrentUser()->isLoggedIn()) {
            Output::addMetadata('user.login.redirect', 'redirect', '/');
            return System::asJson($response);
        }

        $this->render::setActive("noUI");
        $this->render::setMetaTitle("Register | ". ($this->config->app->defaultMetaTitle ?? "Io Dashboard"));
        $components = [];

        $components[] = (new FormComponent())->src(Routes::getRoutePattern('user.display.forms.register'))
            ->title(System::getVariable('user_register_description_text') ?? "Register an account.")
            ->addClass('register-form')->setID('register');
        $components[] = (new BasicLink())
            ->addClass('loginLink')
            ->addRoute(Routes::getRoutePattern('user.display.login'))
            ->addText(System::getVariable('user_register_login_text') ?? 'Got an account? Log in.');

        return System::asJson($response, $this->render::build($components));
    }

    /**
     * @param Request $request
     * @param Response $response
     * @return Response
     */
    public function loginForm(Request $request, Response $response, $args)
    {
        return System::asJson($response, (new loginForm())->loginForm($args));
    }

    /**
     * Forgotten Password Page
     * @param Request $request
     * @param Response $response
     * @return Response
     */
    public function forgotPasswordPage(Request $request, Response $response)
    {
        if (currentUser::getCurrentUser()->isLoggedIn()) {
            Output::addMetadata('user.login.redirect', 'redirect', '/');

            return System::asJson($response);
        }
        $this->render::setActive("noUI");
        $this->render::setMetaTitle("Forgotten Password | ". ($this->config->app->defaultMetaTitle ?? "Io Dashboard"));

        $rowWrapper = (new RowWrapper())->addClass('d-block');
        $rowWrapper->addComponent(
            (new FormComponent())->src(Routes::getRoutePattern('user.display.forms.forgottenPassword'))
            ->title("Enter your email to reset your password.")
                ->addClass('forgotten-password-form')->setID('forgottenPassword')
        );
        $rowWrapper->addComponent(
            (new BasicLink())->addClass('forgottenPasswordLink return mt-5 text-decoration-underline')->addRoute(Routes::getRoutePattern('user.display.login'))->addText(System::getVariable('user_login_return_login_text') ?? 'Return to login')
        );

        $this->output::addResponse($request, $this->render::build([$rowWrapper]));

        return System::asJson($response);
    }

    /**
     * @param Request $request
     * @param Response $response
     * @return Response
     */
    public function forgotPasswordCheckEmail(Request $request, Response $response)
    {
        $this->render::setActive("noUI");
        $this->render::setMetaTitle("Check Email | ". ($this->config->app->defaultMetaTitle ?? "Io Dashboard"));

        $rowWrapper = (new RowWrapper())->addClass('d-block');
        $rowWrapper->addComponent(
            (new ContentTitle())
            ->addContent("Please check your email for further instructions. The email may take upto a few minutes to arrive.")
            ->addClass('text-white')
                ->setColWidth('md', 12)->setID('forgottenPassword')
        );
        $rowWrapper->addComponent(
            (new BasicLink())->addClass('forgottenPasswordLink return mt-5 text-decoration-underline')->addRoute(Routes::getRoutePattern('user.display.login'))->addText(System::getVariable('user_login_return_login_text') ?? 'Return to login')
        );

        $this->output::addResponse($request, $this->render::build([$rowWrapper]));
        return System::asJson($response);
    }

    /**
     * @param Request $request
     * @param Response $response
     * @return Response
     */
    public function forgotPasswordForm(Request $request, Response $response)
    {
        return System::asJson($response, (new forgottenPasswordForm())->forgottenPasswordForm());
    }

    /**
     * @param Request $request
     * @param Response $response
     * @return Response
     */
    public function registerForm(Request $request, Response $response)
    {
        $form = $this->primaryAccountForm(true);
        return System::asJson($response, $form->getBuiltFormArray());
    }

    /**
     * @param Request $request
     * @param Response $response
     * @param array $args
     * @return Response
     * @throws \Exception
     */
    public function userEditForm(Request $request, Response $response, $args = [])
    {
        $form = $this->primaryAccountForm();

        //set form field values
        $user = new userEntity();
        if (isset($args[$user->primaryKey])) {
            $user->load($args[$user->primaryKey]);
            $form->setMethod('PUT');
        }

        $form->setActionUrl(Routes::getRoutePattern('userEntity.put'));

        if ($this->currentUser->isAllowed('ChangeUserActiveState')) {
            $activeField = (new checkboxField('active'))->setLabel('Active');
        } else {
            $activeField = (new inputField('active'))->setInputType('hidden');
        }

        if ($id = $args[$user->primaryKey] ?? false) {
            $form->setActionUrl(Routes::getRoutePattern($user->getEntityName().'.put', ['id' => $id]));

            $form->addField($form->getFormField('first_name')->setValue($user->first_name));
            $form->addField($form->getFormField('last_name')->setValue($user->last_name));
            $form->addField($form->getFormField('email')->setValue($user->email));

            $activeField->setValue($user->active);
        }

        $form->addField($activeField);

        //allow other modules to add form fields.
        $form = Hook::processHook('displayFormRender', $form, $user, 'userEditForm');

        $roles = (new roleEntity())->loadMultiple();
        foreach ($roles as $role) {
            //for users that can't change roles, show labels for those that they do have.
            $showLabel = false;
            if ($this->currentUser->isAllowed('UpdateRoles') || $this->currentUser->isAllowed('AssignRole_'.$role->id)) {
                $roleField = (new checkboxField('role_' . $role->id))->setLabel($role->name);
            } else {
                $showLabel = true;
                $roleField = (new inputField('role_' . $role->id));
                $roleField->setInputType('hidden');
            }
            if (($user->id ?? false) && is_array($user->roles) && ($user && in_array($role->id, $user->roles))) {
                //allows us to show the label only for those without the role to change.
                //Only shows labels for the roles they have.
                if ($showLabel) {
                    $roleField->setLabel($role->name);
                }
                $roleField->setValue($role->id);
            }
            $form->addField($roleField, 'roles');
        }

        //set role values
        $this->output::addResponse($request, $form->getBuiltFormArray());
        return System::asJson($response);
    }

    /**
     * @param Request $request
     * @param Response $response
     * @return Response
     */
    public function userHome(Request $request, Response $response)
    {
        $rowWrapper = (new RowWrapper())->setColWidth('sm', 12)->addClass('manageForm-wrapper');
        $rowWrapper->setColWidth('sm', 12)->addClass('manageForm-wrapper');
        $rowWrapper->addComponent(new userEntityDisplayTile());
        if ($this->currentUser->isAllowed('ViewRoles')) {
            $rowWrapper->addComponent(new roleEntityDisplayTile());
        }

        $this->render::setPageTitle('User Dashboard');
        $this->output::addResponse($request, $this->render::build([$rowWrapper]));
        return System::asJson($response);
    }

    /**
     * @param Request $request
     * @param Response $response
     * @return Response
     * @throws \Exception
     */
    public function userList(Request $request, Response $response)
    {
        $filterForm = new EntityFilteredDisplayTable((new userEntity()));
        $filterForm->addFilterForm('user.display.forms.filters.userFilter');

        //render
        $this->render::setPageTitle('User List');
        $this->output::addResponse($request, $this->render::build($filterForm));
        return System::asJson($response);
    }

    public function create(Request $request, Response $response, $args)
    {
        $title = 'Create new user';
        if ($id = $args['id'] ?? false) {
            $user = new userEntity();
            $user->load($id);
            $title = 'Update ' . $user->getNiceName();
        }

        $form = (new FormComponent())->setID('userCreate')->src(Routes::getRoutePattern('user.display.forms.createUpdate', $args));
        $this->render::setPageTitle($title);
        $this->output::addResponse($request, $this->render::build([genericComponents::dashboardBlockWrapper([$form])]));
        return System::asJson($response);
    }

    /**
     * @param Request $request
     * @param Response $response
     * @param array $args
     * @return Response
     * @throws \Exception
     */
    public function updateView(Request $request, Response $response, $args = [])
    {
        $title = 'Create a user';
        if ($id = $args['id'] ?? false) {
            $userEntity = new userEntity();
            $userEntity->load($id);
            $title = 'Edit ('.$id.') '. $userEntity->getNiceName();
        }

        $form = (new FormComponent())->setID('roleCreate')->src(Routes::getRoutePattern('user.display.forms.createUpdate', $args));
        $this->render::setPageTitle($title);
        $this->output::addResponse($request, $this->render::build($form));
        return System::asJson($response);
    }

    /**
     * @param bool $passwordRequired
     * @return formEntity
     */
    protected function primaryAccountForm($passwordRequired = false): formEntity
    {
        $form = new formEntity();
        $form->setId('login');
        $form->setMethod('post');
        $form->setActionUrl(Routes::getRoutePattern('userEntity.post'));

        $form->addField((new inputField('first_name'))->setLabel('First Name')->setPlaceholder('First Name')->isRequired());
        $form->addField((new inputField('last_name'))->setLabel('Surname')->setPlaceholder('Surname')->isRequired());
        $form->addField((new inputField('email'))->setLabel('Email')->setPlaceholder('Email')->setInputType('email')->isRequired());

        $password = (new inputField('password'))->setLabel('Password')->setPlaceholder('Password')->setInputType('password')->addValidation("minLength", ["min" => 6]);
        $confirmPass = (new inputField('confirm_password'))->setLabel('Confirm Password')->setPlaceholder('Confirm Password')->setInputType('password')->addValidation("minLength", ["min" => 6]);
        if ($passwordRequired) {
            $password->isRequired();
            $confirmPass->isRequired();
        }
        $form->addField($password);
        $form->addField($confirmPass);

        return $form;
    }

    /**
     * @param Request $request
     * @param Response $response
     * @param $args
     * @return Response
     */
    public function userSummery(Request $request, Response $response, $args)
    {
        $structure = [
            'tableHeader' => [
                'User ID',
                'Name',
                'Active',
                'Email',
                'Last Logged In',
                'Roles'
            ],
            'fields' => [
                'id',
                'first_name',
                'active',
                'email',
                'last_login',
                'roles'
            ],
            'callables' => [
                'field__last_login' => function ($key, $entityId, $row) : string {
                    return \apexl\Io\modules\userDisplay\callbacks\tableRowAlters::userShortTable($key, $entityId, $row);
                },
                'field__first_name' => function ($key, $entityId, $row) : string {
                    return \apexl\Io\modules\userDisplay\callbacks\tableRowAlters::userShortTable($key, $entityId, $row);
                },
                'field__roles' => function ($key, $entityId, $row) : string {
                    return \apexl\Io\modules\userDisplay\callbacks\tableRowAlters::userShortTable($key, $entityId, $row);
                }
            ]
        ];
        return $this->getEntityData($request, $response, $args, (new userEntity()), [], $structure);
    }
}
