<?php

namespace apexl\Io\modules\user\controllers;

use apexl\Io\exceptions\RecordNotFoundException;
use apexl\Io\includes\Controller;
use apexl\Io\includes\RouteManager;
use apexl\Io\modules\notifier\exceptions\notifierException;
use apexl\Io\modules\user\Actions\ForgotPasswordAction;
use apexl\Io\modules\user\entities\userEntity;
use apexl\Io\modules\user\Request\ForgotPasswordRequest;
use apexl\Io\modules\user\RequestHelper\ForgotPasswordRequestHelper;
use JsonException;
use Psr\Http\Message\ResponseInterface;

final readonly class ForgotPasswordController extends Controller
{
    /**
     * @throws notifierException
     * @throws JsonException
     */
    public function __invoke(
        ForgotPasswordRequest $request,
        ResponseInterface $response,
        ForgotPasswordAction $forgotPasswordAction,
        RouteManager $routeManager,
    ): ResponseInterface {
        try {
            $user = userEntity::from('email', $request->email());

            $forgotPasswordAction->handle($user);
        } catch (RecordNotFoundException) {
            // Account-enumeration guard: an anonymous visitor must not be able
            // to tell, from the response, whether an email address belongs to
            // a real account. Log server-side only (never in the response
            // body/status/redirect target) and fall through to the exact same
            // check-email redirect the success path below takes.
            error_log(sprintf(
                'Forgot-password request for unknown email: %s',
                $request->email()
            ));
        }

        $this->output->addMetadata(
            'user.forgot-password.redirect',
            'redirect',
            $routeManager->getRoutePattern('user.display.forgot-password.check-email')
        );

        // Every other action controller in this module puts a success envelope on the
        // response (LoginController, ForgotPasswordWithLinkController, rolesController,
        // the 2FA set); this one never did, so consumers got a body with no `success`
        // key to branch on. Set unconditionally, after both branches have converged, so
        // the known- and unknown-email responses stay byte-identical and the
        // enumeration guard above is untouched.
        $this->output->addResponse($request);

        return $this->json($response);
    }
}
