<?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\CoreBundle\Settings;

use Doctrine\ORM\EntityManagerInterface;
use Ferienpass\CoreBundle\Entity\IntegrationCredential;
use Ferienpass\CoreBundle\Entity\PageConfig;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;

class SettingsManager
{
    private const CACHE_KEY_PAGE_CONFIG = 'page_config';
    private const CACHE_KEY_PAYMENT_CONFIG = 'payment_config_';

    public function __construct(
        private readonly EntityManagerInterface $entityManager,
        private readonly CacheInterface $appConfigCache,
        #[Autowire(env: 'APP_SECRET')]
        private readonly string $appSecret
    ) {
    }

    public function getPageConfig(): PageConfig
    {
        return $this->appConfigCache->get(self::CACHE_KEY_PAGE_CONFIG, function (ItemInterface $item) {
            $config = $this->entityManager->getRepository(PageConfig::class)->findOneBy([]);
            if (null === $config) {
                $config = new PageConfig();
                $config->setPageTitle('Ferienpass');
                $config->setAdminEmail('admin@ferienpass.online');
            }

            return $config;
        });
    }

    public function savePageConfig(PageConfig $config): void
    {
        $this->entityManager->persist($config);
        $this->entityManager->flush();

        $this->appConfigCache->delete(self::CACHE_KEY_PAGE_CONFIG);
    }

    public function getPaymentProviderConfig(string $providerName): ?array
    {
        return $this->appConfigCache->get(self::CACHE_KEY_PAYMENT_CONFIG.$providerName, function (ItemInterface $item) use ($providerName) {
            /** @var PaymentProviderConfig|null $config */
            $config = $this->entityManager->getRepository(IntegrationCredential::class)->find($providerName);

            if (null === $config || null === $config->getCredentials()) {
                return null;
            }

            return $this->decrypt($config->getCredentials());
        });
    }

    public function savePaymentProviderConfig(string $providerName, array $credentials): void
    {
        /** @var PaymentProviderConfig|null $config */
        $config = $this->entityManager->getRepository(IntegrationCredential::class)->find($providerName);

        if (null === $config) {
            $config = new IntegrationCredential($providerName);
        }

        $config->setCredentials($this->encrypt($credentials));

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

        $this->appConfigCache->delete(self::CACHE_KEY_PAYMENT_CONFIG.$providerName);
    }

    private function encrypt(array $data): array
    {
        $json = json_encode($data);
        $nonce = random_bytes(\SODIUM_CRYPTO_SECRETBOX_NONCEBYTES);
        $key = $this->getEncryptionKey();

        $ciphertext = sodium_crypto_secretbox($json, $nonce, $key);

        return [
            'ciphertext' => base64_encode($ciphertext),
            'nonce' => base64_encode($nonce),
        ];
    }

    private function decrypt(array $encrypted): array
    {
        $ciphertext = base64_decode($encrypted['ciphertext'], true);
        $nonce = base64_decode($encrypted['nonce'], true);
        $key = $this->getEncryptionKey();

        $json = sodium_crypto_secretbox_open($ciphertext, $nonce, $key);

        if (false === $json) {
            throw new \RuntimeException('Failed to decrypt credentials');
        }

        return json_decode($json, true);
    }

    private function getEncryptionKey(): string
    {
        // Deriving a 32-byte key from APP_SECRET
        return hash('sha256', $this->appSecret, true);
    }
}
