<?php declare(strict_types=1);
/*
 * This file is part of the SplendidBear Websites' projects.
 *
 * Copyright (c) 2026 @ www.splendidbear.org
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace App\Service;

use App\Entity\User;
use App\Entity\WebAuthnCredential;
use App\Repository\WebAuthnCredentialRepository;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use JsonException;
use RuntimeException;

/**
 * Class WebAuthnService
 *
 * @package  App\Service
 * @author   Lang <https://www.splendidbear.org>
 * @category Class
 * @license  https://www.gnu.org/licenses/lgpl-3.0.en.html GNU Lesser General Public License
 * @link     www.splendidbear.org
 * @since    2026. 04. 12.
 */
readonly final class WebAuthnService
{
    public function __construct(
        private WebAuthnCredentialRepository $credentialRepository,
        private EntityManagerInterface       $entityManager,
    ) {
    }

    public function saveCredential(User $user, array $credentialData, string $name): WebAuthnCredential
    {
        $credential = new WebAuthnCredential();
        $credential->user = $user;
        $credential->credentialData = json_encode($credentialData);
        $credential->credentialName = $name;
        $credential->isBackupEligible = $credentialData['isBackupEligible'] ?? false;
        $credential->isBackupAuthenticated = $credentialData['isBackupAuthenticated'] ?? false;

        $this->entityManager->persist($credential);
        $this->entityManager->flush();

        return $credential;
    }

    public function getCredentialsForUser(User $user): array
    {
        return $this->credentialRepository->findByUser($user);
    }

    public function deleteCredential(int $id, User $user): bool
    {
        $credential = $this->credentialRepository->find($id);

        if ($credential === null || $credential->user !== $user) {
            return false;
        }

        $this->entityManager->remove($credential);
        $this->entityManager->flush();

        return true;
    }

    public function renameCredential(int $id, User $user, string $name): bool
    {
        $credential = $this->credentialRepository->find($id);

        if ($credential === null || $credential->user !== $user) {
            return false;
        }

        $credential->credentialName = $name;
        $this->entityManager->flush();

        return true;
    }

    public function getPublicKeyCredentialLoader(): null
    {
        /**
         * Return a simple object - the actual WebAuthn validation
         * would be done on the client side for now
         */
        return null;
    }

    public function getAllCredentialSources(User $user): array
    {
        $credentials = $this->credentialRepository->findByUser($user);
        $sources = [];

        foreach ($credentials as $credential) {
            $data = $credential->credentialData;

            if ($data === null) {
                continue;
            }

            try {
                $sources[] = json_decode($data, true, 512, JSON_THROW_ON_ERROR);
            } catch (JsonException $e) {
                throw new RuntimeException(
                    "Failed to decode credential data for credential ID {$credential->id}: {$e->getMessage()}",
                );
            }
        }

        return $sources;
    }

    public function updateLastUsedAt(string $credentialId, User $user): void
    {
        $credentials = $this->credentialRepository->findByUser($user);

        foreach ($credentials as $credential) {
            $data = json_decode($credential->credentialData ?? '{}', true);

            if (($data['id'] ?? null) !== $credentialId) {
                continue;
            }

            $credential->lastUsedAt = new DateTime();
            $this->entityManager->flush();
            break;
        }
    }

    public function findUserByCredentialId(string $credentialId): ?User
    {
        $allCredentials = $this->credentialRepository->findAll();

        foreach ($allCredentials as $credential) {
            $data = json_decode($credential->credentialData ?? '{}', true);

            if (($data['id'] ?? null) !== $credentialId) {
                continue;
            }

            return $credential->user;
        }

        return null;
    }
}




