<?php

namespace apexl\Io\modules\user\controllers;

use apexl\hashing\Hash;
use apexl\Io\includes\Controller;
use apexl\Io\includes\Hook;
use apexl\Io\includes\Routes;
use apexl\Io\includes\System;
use apexl\Io\modules\email\services\templateService;
use apexl\Io\modules\user\entities\refreshTokenEntity;
use apexl\Io\modules\user\entities\sessionEntity;
use apexl\Io\modules\user\entities\userEntity;
use apexl\Io\modules\user\entities\userNoteEntity;
use apexl\Io\modules\user\services\currentUser;
use apexl\Io\modules\user\services\twoFactorAuth\TwoFactorAuthService;
use apexl\Io\modules\user\services\userTools;
use apexl\Io\services\Logger;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

class twoFactorAuthController extends Controller
{
    /** @var userEntity */
    protected userEntity $currentUser;
    protected $twoFactorAuthService;

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

    public function completeSetup(Request $request, Response $response, $args)
    {
        $body = $request->getParsedBody();

        $this->twoFactorAuthService->log('process_complete_setup', $body);

        if (empty($body) || !isset($body->twofa_method)) {
            $this->output::addMessage('user.two-factor-auth.complete-setup.validation', 'error', "Missing data - cannot complete setup.");
            $this->output::addResponse($request, [], false); //added so we can hook into this elsewhere.
            return System::asJson($response, [], 400);
        }

        $methodService = $this->twoFactorAuthService->getMethodService($body->twofa_method);
        $result = $methodService->processSetup($body->digit1.$body->digit2.$body->digit3.$body->digit4.$body->digit5.$body->digit6);

        if (!$result->isValid) {
            // DO NOT enable 2FA. Return an error so the user can try again.
            $this->output::addMessage($this->path->getRouteName($request).'.failure', 'error', $result->error ?? "Invalid code. Please try again.");
            $this->output::addResponse($request, [], false); //added so we can hook into this elsewhere.
            return System::asJson($response, [], 400);
        }

        $this->output::addMetadata('user.two-factor-auth.complete-setup.redirect', 'redirect', Routes::getRoutePattern('user.display.two-factor-auth.setup-complete', ['meta' => base64_encode(json_encode($result))]));
        return System::asJson($response, []);

    }

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

        $body = $request->getParsedBody();

        $this->twoFactorAuthService->log('process_authentication', $body);

        if (empty($body) || !isset($body->twofa_method)) {
            $this->output::addMessage('user.two-factor-auth.authenticate.validation', 'error', "Missing data - cannot complete setup.");
            $this->output::addResponse($request, [], false); //added so we can hook into this elsewhere.
            return System::asJson($response, [], 400);
        }

        $methodService = $this->twoFactorAuthService->getMethodService($body->twofa_method);
        $result = $methodService->processAuthentication($request, $body->digit1.$body->digit2.$body->digit3.$body->digit4.$body->digit5.$body->digit6);
        if ($result->isValid) {
            $this->output::addMetadata('user.two-factor-auth.authenticate', 'redirect', '/'); // success - redirect to dashboard
            return System::asJson($response, []);
        } else {
            $this->output::addMetadata('user.two-factor-auth.authenticate', 'redirect', Routes::getRoutePattern('user.display.two-factor-auth.authenticate'));
            return System::asJson($response, ['message' => $result->error ?? 'Two Factor Authentication Required'], 401);
        }

    }

    public function verifyRecoveryCode(Request $request, Response $response, $args)
    {
        $body = $request->getParsedBody();

        $this->twoFactorAuthService->log('process_verify_recovery_code', $body);

        if (empty($body->recovery_code)) {
            $this->output::addMessage('user.two-factor-auth.verify-code.validation', 'error', "Recovery code is required.");
            $this->output::addResponse($request, [], false); //added so we can hook into this elsewhere.
            return System::asJson($response, [], 400);
        }

        $currentUser = currentUser::getCurrentUser();
        $result = $this->twoFactorAuthService->validateRecoveryCode($request, $currentUser->id, $body->recovery_code);

        if (!$result) {
            $this->output::addMessage('user.two-factor-auth.verify-code.validation', 'error', "The recovery code you provided was invalid. Please try a different code or contact support for further assistance.");
            $this->output::addResponse($request, [], false);
            return System::asJson($response, [], 400);
        }

        $this->output::addMetadata('user.two-factor-auth.verify-code', 'redirect', Routes::getRoutePattern('user.display.two-factor-auth.recovery-complete')); // success - redirect to dashboard
        return System::asJson($response, []);

    }

}
