<?php

declare(strict_types=1);

/*
 * This file is part of the VITICORE2 package.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Commerce\Catalog\Configuration;

use CpCreation\VitiCore\Commerce\Catalog\Model\CatalogConfiguration;
use CpCreation\VitiCore\Commerce\Catalog\Repository\CatalogConfigurationStore;
use Psr\Log\LoggerInterface;
use Symfony\Contracts\Service\ResetInterface;

final class DoctrineCatalogConfigurationProvider implements CatalogConfigurationProvider, ResetInterface
{
    private bool $loaded = false;

    private ?CatalogConfigurationSnapshot $snapshot = null;

    public function __construct(
        private readonly CatalogConfigurationStore $store,
        private readonly CatalogSecretCipher $secretCipher,
        private readonly bool $kernelDebug,
        private readonly ?LoggerInterface $logger = null,
    ) {}

    public function current(): ?CatalogConfigurationSnapshot
    {
        if ($this->loaded) {
            return $this->snapshot;
        }

        $this->loaded = true;
        $configuration = $this->store->get();
        if (!$configuration instanceof CatalogConfiguration) {
            return null;
        }

        $enrollmentToken = null;
        $webhookSecret = null;
        if ($configuration->isHubConnectionEnabled()) {
            $expiresAt = $configuration->getEnrollmentTokenExpiresAt();
            if ($expiresAt instanceof \DateTimeImmutable && $expiresAt > new \DateTimeImmutable('now', new \DateTimeZone('UTC'))) {
                $enrollmentToken = $this->revealSecret(
                    fn(): ?string => $configuration->revealEnrollmentToken($this->secretCipher),
                    'JWT d’enrôlement',
                );
            }
            $webhookSecret = $this->revealSecret(
                fn(): ?string => $configuration->revealWebhookSecret($this->secretCipher),
                'secret webhook',
            );
        }

        return $this->snapshot = new CatalogConfigurationSnapshot(
            enabled: $configuration->isEnabled(),
            monitoringEnabled: $configuration->isMonitoringEnabled(),
            hubUrl: $this->transportUrl($configuration->getHubUrl()),
            instanceId: $configuration->getInstanceId()->toString(),
            currency: $configuration->getCurrency(),
            authAudience: $configuration->getAuthAudience(),
            enrollmentToken: $enrollmentToken,
            webhookSecret: $webhookSecret,
            webhookUrl: $this->transportUrl($configuration->getWebhookUrl()),
            jwtIssuer: $configuration->getJwtIssuer(),
            jwtKeyId: $configuration->getJwtKeyId(),
            jwtPublicKeyPem: $configuration->getJwtPublicKeyPem(),
        );
    }

    public function reset(): void
    {
        $this->loaded = false;
        $this->snapshot = null;
    }

    /** @param \Closure(): ?string $reveal */
    private function revealSecret(\Closure $reveal, string $label): ?string
    {
        try {
            return $reveal();
        } catch (\RuntimeException|\LogicException $exception) {
            $this->logger?->error('Impossible de déchiffrer un secret de la configuration Commerce.', [
                'secret' => $label,
                'exception' => $exception::class,
            ]);

            return null;
        }
    }

    private function transportUrl(?string $url): ?string
    {
        if (!$this->kernelDebug || !is_file('/.dockerenv') || null === $url) {
            return $url;
        }

        $parts = parse_url($url);
        if (!\is_array($parts) || !\in_array(mb_strtolower((string) ($parts['host'] ?? '')), ['localhost', '127.0.0.1', '::1'], true)) {
            return $url;
        }

        $scheme = (string) ($parts['scheme'] ?? '');
        if (!\in_array(mb_strtolower($scheme), ['http', 'https'], true)) {
            return $url;
        }

        $authority = 'host.docker.internal';
        if (isset($parts['port'])) {
            $authority .= ':' . $parts['port'];
        }

        return $scheme . '://' . $authority . ($parts['path'] ?? '');
    }
}
