<?php

namespace apexl\Io\modules\userDisplay\controllers;

use apexl\Io\includes\Controller;
use apexl\Io\includes\Routes;
use apexl\Io\includes\System;
use apexl\Io\modules\display\components\BasicLink;
use apexl\Io\modules\display\components\BasicTable;
use apexl\Io\modules\display\components\CardTile;
use apexl\Io\modules\display\components\ColWrapper;
use apexl\Io\modules\display\components\ContentTitle;
use apexl\Io\modules\display\components\FormComponent;
use apexl\Io\modules\display\components\RowWrapper;
use apexl\Io\modules\display\services\Render;
use apexl\Io\modules\formbuilder\entities\formEntity;
use apexl\Io\modules\formbuilder\includes\inputField;
use apexl\Io\modules\user\services\currentUser;
use apexl\Io\modules\user\services\twoFactorAuth\methods\Totp2FaMethod;
use apexl\Io\modules\user\services\twoFactorAuth\TwoFactorAuthService;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

class twoFactorAuthController extends Controller
{
    protected $currentUser;
    protected $twoFactorAuthService;

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

    public function setup(Request $request, Response $response, $args)
    {

        $method = $args['method'] ?? ($this->config->app->twoFactorAuth->methods[0] ?? null);

        $this->twoFactorAuthService->log('load_setup_page', ['method' => $method]);

        if ($method) {
            $methodService = $this->twoFactorAuthService->getMethodService($method);
            $components = $methodService->setup($request, $response, $args);
            
        } else {
            // 2FA not enabled - user should never get to this route
        }

        $this->render::setPageTitle('Set up Two-factor Authentication');
        $this->output::addResponse($request, $this->render::build($components));
        return System::asJson($response);
    }

    public function recoveryComplete(Request $request, Response $response, $args)
    {

        $method = $args['method'] ?? ($this->config->app->twoFactorAuth->methods[0] ?? null);

        $this->twoFactorAuthService->log('load_recoveryComplete_page', ['method' => $method]);

        if ($method) {
            $methodService = $this->twoFactorAuthService->getMethodService($method);
            $components = $methodService->setup($request, $response, $args);

        } else {
            // 2FA not enabled - user should never get to this route
        }

        $leadComponent = (new ColWrapper())
            ->addComponent(
                (new ContentTitle())
                    ->addContent("You have successfully used one of your recovery codes. The code you used has now been disabled, so you won't be able to use it again.<br>If you no longer have access to your chosen authentication method, it's important that you set up a new one:<br><br><span class=\"text-danger fw-bold\">Please make sure you delete any existing GlobalM2M SIM Dashboard entries from your Authenticator App before continuing.</span>")
            );

        $this->render::setPageTitle('Recovery Complete - set up new Two-factor Authentication Method');
        $this->output::addResponse($request, $this->render::build(array_merge([$leadComponent], $components)));
        return System::asJson($response);
    }

    public function authenticate(Request $request, Response $response, $args)
    {

        $methodService = $this->twoFactorAuthService->getUser2FaMethod();

        $this->twoFactorAuthService->log('load_authenticate_page', ['method' => ($methodService ? $methodService::getMethodName() : null)]);

        $components = $methodService->authenticate($request, $response, $args) ?? [];

        $components[] = (new ColWrapper())
            ->addClass('text-center')
            ->addComponent(
                (new BasicLink())
                    ->addText('<strong>No longer have access to your Authentication Method?</strong><br>Use one of your one-time recovery codes')
                    ->addRoute(Routes::getRoutePattern('user.display.two-factor-auth.enter-backup-code'))
                    ->addClass('btn btn-secondary btn-sm py-2 px-4 text-center')
            );

        $this->render::setPageTitle('Complete Two-factor Authentication');
        $this->output::addResponse($request, $this->render::build($components));
        return System::asJson($response);
    }

    public function enterBackupCode(Request $request, Response $response, $args)
    {
        $this->twoFactorAuthService->log('load_backup_code_insert_page');

        $components[] = (new RowWrapper())
            ->addClass('justify-content-center')
            ->addComponent(
                (new ColWrapper())
                    ->addClass('border p-4 col-md-6 text-center')
                    ->addComponents([
                        (new ContentTitle())
                            ->addTitle('Enter One-time Recovery Code')
                            ->addContent('Please enter one of your one-time-use recovery codes. These were provided when you first set up your Two Factor Authentication method.'),
                        (new FormComponent())
                            ->src(Routes::getRoutePattern('user.display.forms.enter2faBackupCode'))
                    ])
            );

        $this->render::setPageTitle('Two-factor Authentication Recovery');
        $this->output::addResponse($request, $this->render::build($components));
        return System::asJson($response);
    }
    
    public function enterSixDigitCodeForm(Request $request, Response $response, $args)
    {
        $form = new formEntity();
        $form->setId('2fa-enter-code');
        $form->setMethod('post');
        $actionRoute = $args['entryType'] == 'setup' ? 'user.two-factor-auth.complete-setup' : 'user.two-factor-auth.authenticate';
        $form->setActionUrl(Routes::getRoutePattern($actionRoute));
        $form->setSubmitText('Submit Code');
        $form->disableSubmitButtonOnLoad();

        foreach (range(1, 6) as $i) {
            $field = (new inputField("digit{$i}"))->addClass('digit-field')->setPlaceholder('-')->isRequired();
            if ($i < 6) {
                $field->setNextFieldId("digit".($i+1));
            }
            if ($i > 1) {
                $field->setPrevFieldId("digit".($i-1));
            }
            $form->addField($field);
        }

        $form->addField((new inputField('twofa_method'))->setInputType('hidden')->setValue(Totp2FaMethod::getMethodName())->isRequired());

        return System::asJson($response, $form->getBuiltFormArray());
    }

    public function enterBackupCodeForm(Request $request, Response $response, $args)
    {
        $form = new formEntity();
        $form->setId('2fa-enter-recovery-code');
        $form->setMethod('post');
        $form->setActionUrl(Routes::getRoutePattern('user.two-factor-auth.verify-recovery-code'));
        $form->setSubmitText('Submit Recovery Code');

        $form->addField((new inputField('recovery_code'))->setLabel('Recovery Code:')->addClass('text-center')->isRequired());


        return System::asJson($response, $form->getBuiltFormArray());
    }


    public function setupComplete(Request $request, Response $response, $args)
    {
        $meta = json_decode(base64_decode($args['meta']));

        $this->twoFactorAuthService->log('load_setup_complete_page_page');

        $components = [];
        $components[] = (new ContentTitle())
            ->addTitle('Two Factor Authentication has been set up Successfully')
            ->addContent('From now on when logging in you will be asked to enter a 6 digit code provided by your chosen authenticator app');
        if (isset($meta->recoveryCodes) && count($meta->recoveryCodes) > 0){
            $data = ['rows' => array_chunk($meta->recoveryCodes,4)];
            $components[] = (new ColWrapper())
                ->addComponent(
                    (new CardTile())
                    ->addTitle('Recovery codes')
                        ->addComponent(
                            (new ContentTitle())
                                ->addClass('mt-3')
                                ->addContent('<strong><u>Please safely store the following recovery codes in a safe location</u></strong>.
<br>They will allow you to gain access to your account if you lose access to your Authenticator App.
                                    <br>They will no longer be accessible after leaving this page.'
                                )
                        )
                        ->addComponent(
                            (new BasicTable($data))
                        )
                    )
                ->addComponent(
                    (new BasicLink())
                        ->addRoute('/')
                        ->addText('<strong>I have safely stored my recovery codes</strong><br>Take me to my dashboard')
                        ->addClass('text-center mt-4 sign-in-link btn btn-primary btn-lg')
                );
        } else {
            $components[] = (new ColWrapper())
                ->addComponent(
                    (new BasicLink())
                        ->addRoute('/')
                        ->addText('Return to the dashboard')
                        ->addClass('text-center mt-4 sign-in-link btn btn-primary btn-lg')
                );
        }


        $this->render::setPageTitle('Setup Complete');
        $this->output::addResponse($request, $this->render::build($components));
        return System::asJson($response);
    }
}
