<?php

declare(strict_types=1);

namespace apexl\Io\modules\user\services;

use apexl\Config\ConfigurationInterface;
use apexl\Config\Exceptions\MissingConfigException;
use apexl\Io\modules\user\entities\userEntity;
use DateTimeImmutable;
use DateTimeInterface;
use Firebase\JWT\JWT;

final readonly class JwtCreator
{
    public function __construct(private ConfigurationInterface $config) {}

    /**
     * @throws MissingConfigException
     * @noinspection PhpDocMissingThrowsInspection
     */
    public function forUser(
        userEntity $user,
        string $sessionId,
        ?DateTimeInterface $issuedAt = null,
        ?string $serverName = null
    ): string {

        $secretKey = $this->config->get('auth.jwt.secret_key');
        $algorithm = $this->config->get('auth.jwt.algorithm');
        $lifetime = $this->config->get('auth.jwt.lifetime');

        $issuedAt = $this->resolveIssuedAt($issuedAt);

        // SERVER_NAME is unset on SAPIs that don't populate it (CLI, some test
        // runners), which previously left `iss` null — a latent prod bug and the
        // source of the flaky InstallWizardPasswordQuotingTest (Plan 007 §6).
        // Fall back to the configured backend domain's host, then 'localhost'.
        $serverName ??= $_SERVER['SERVER_NAME'] ?? $this->fallbackIssuer();
        /** @noinspection PhpUnhandledExceptionInspection */
        $expire = $issuedAt->modify(sprintf('+%s seconds', $lifetime))->getTimestamp();

        $payload = [
            'userId' => $user->id,
            'sessionId' => $sessionId,
            'iat' => $issuedAt->getTimestamp(),
            'exp' => $expire,
            'nbf' => $issuedAt->getTimestamp(),
            'iss' => $serverName,
        ];

        return JWT::encode($payload, $secretKey, $algorithm);
    }

    private function fallbackIssuer(): string
    {
        if ($this->config->has('http.backend_domain')) {
            $host = parse_url((string) $this->config->get('http.backend_domain'), PHP_URL_HOST);
            if (is_string($host) && $host !== '') {
                return $host;
            }
        }

        return 'localhost';
    }

    private function resolveIssuedAt(?DateTimeInterface $issuedAt): DateTimeImmutable
    {
        $issuedAt ??= new DateTimeImmutable();

        if ($issuedAt instanceof \DateTime) {
            $issuedAt = DateTimeImmutable::createFromMutable($issuedAt);
        }

        return $issuedAt;
    }

}