<?php

declare(strict_types=1);

/*
 * This file is part of the Ferienpass package.
 *
 * (c) Richard Henkenjohann <richard@ferienpass.online>
 *
 * For more information visit the project website <https://ferienpass.online>
 * or the documentation under <https://docs.ferienpass.online>.
 */

namespace Ferienpass\CmsBundle\Controller\Frontend;

use Contao\CoreBundle\Entity\WebauthnCredential;
use Contao\CoreBundle\Repository\WebauthnCredentialRepository;
use Ferienpass\CmsBundle\EventListener\WebauthnCredentialNamer;
use Ferienpass\CoreBundle\Entity\User;
use Ferienpass\CoreBundle\Repository\WebauthnUserEntityRepository;
use Ferienpass\CoreBundle\Ux\Flash;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser;
use Symfony\Component\Translation\TranslatableMessage;

class PasskeyAccountController extends AbstractController
{
    #[Route('/account/passkey/name', name: 'cms_account_passkey_name', methods: ['POST'])]
    public function setName(Request $request, #[CurrentUser] ?User $user): Response
    {
        if (!$user instanceof User) {
            return new JsonResponse(['error' => 'unauthorized'], Response::HTTP_UNAUTHORIZED);
        }

        $request->getSession()->set(WebauthnCredentialNamer::SESSION_KEY, (string) $request->getPayload()->get('name'));

        return new JsonResponse(['status' => 'ok']);
    }

    #[Route('/account/passkey/{id}/delete', name: 'cms_account_passkey_delete', methods: ['POST'])]
    public function delete(
        string $id,
        #[CurrentUser]
        ?User $user,
        #[Autowire(service: 'contao.repository.webauthn_credential')]
        WebauthnCredentialRepository $credentials,
        WebauthnUserEntityRepository $userEntities,
    ): Response {
        if (!$user instanceof User) {
            throw $this->createAccessDeniedException();
        }

        // CSRF: the delete form carries Contao's REQUEST_TOKEN, validated
        // globally by RequestTokenListener before this action runs (the route is
        // _scope=frontend). Same model as the other account POST forms.

        $credential = $credentials->find($id);
        if (!$credential instanceof WebauthnCredential || $credential->userHandle !== $user->getUuid()->toRfc4122()) {
            throw $this->createNotFoundException();
        }

        // Never remove the last passkey — the member could lock themselves out.
        $userEntity = $userEntities->findOneByUsername($user->getUsername());
        $all = null !== $userEntity ? $credentials->findAllForUserEntity($userEntity) : [];

        if (\count($all) <= 1) {
            $this->addFlash(...Flash::error()->text(new TranslatableMessage('userAccount.passkeys.flash.lastError', [], 'cms'))->create());
        } else {
            $credentials->remove($credential);
            $this->addFlash(...Flash::confirmation()->text(new TranslatableMessage('userAccount.passkeys.flash.deleted', [], 'cms'))->create());
        }

        // SECURITY_REVIEW FP-024: Redirect to a fixed internal route instead of
        // the attacker-controllable Referer header (open redirect). The passkeys
        // list is rendered on the "user_account" page under the "account" alias.
        return $this->redirectToRoute('cms', ['to' => 'user_account', 'alias' => 'account']);
    }
}
