<?php

declare(strict_types=1);

namespace App\Controller\Frontend\Account;

use App\Container\EntityManagerAwareTrait;
use App\Controller\SingleActionInterface;
use App\Entity\Enums\LoginTokenTypes;
use App\Entity\Repository\UserLoginTokenRepository;
use App\Http\Response;
use App\Http\ServerRequest;
use InvalidArgumentException;
use Psr\Http\Message\ResponseInterface;
use Throwable;

final class LoginTokenAction implements SingleActionInterface
{
    use EntityManagerAwareTrait;

    public function __construct(
        private readonly UserLoginTokenRepository $loginTokenRepo,
    ) {
    }

    public function __invoke(
        ServerRequest $request,
        Response $response,
        array $params
    ): ResponseInterface {
        $flash = $request->getFlash();

        /** @var string $token */
        $token = $params['token'];

        $loginToken = $this->loginTokenRepo->authenticate($token);

        if (null === $loginToken) {
            $flash->error(
                message: __('Invalid token specified.'),
            );

            return $response->withRedirect($request->getRouter()->named('account:login'));
        }

        $user = $loginToken->getUser();

        // Magic login type tokens just redirect to the dashboard.
        if ($loginToken->type === LoginTokenTypes::Login) {
            $request->getAuth()->setUser($user);

            $this->loginTokenRepo->revokeForUser($user);

            $flash->success(
                message: __('Logged in using login token.'),
            );

            return $response->withRedirect($request->getRouter()->named('dashboard'));
        }

        $csrf = $request->getCsrf();
        $error = null;

        if ($request->isPost()) {
            try {
                $data = $request->getParams();

                $csrf->verify($data['csrf'] ?? null, 'recover');

                if (empty($data['password'])) {
                    throw new InvalidArgumentException('Password required.');
                }

                $user->setNewPassword($data['password']);
                $user->two_factor_secret = null;

                $this->em->persist($user);
                $this->em->flush();

                $request->getAuth()->setUser($user);

                $this->loginTokenRepo->revokeForUser($user);

                $flash->success(
                    message: __('Your password has been updated.'),
                    title: __('Logged in using account recovery token')
                );

                return $response->withRedirect($request->getRouter()->named('dashboard'));
            } catch (Throwable $e) {
                $error = $e->getMessage();
            }
        }

        return $request->getView()->renderVuePage(
            response: $response,
            component: 'Recover',
            id: 'account-recover',
            layout: 'minimal',
            title: __('Recover Account'),
            props: [
                'csrf' => $csrf->generate('recover'),
                'error' => $error,
            ]
        );
    }
}
