<?php

namespace App\Security;

use App\Entity\Device;

final class DeviceCredentialService
{
    public function __construct(
        private readonly string $deviceCredentialPepper,
        private readonly string $devicePairingPepper,
        private readonly int $deviceSessionTtlDays,
        private readonly int $devicePairingCodeTtlMinutes,
    ) {
    }

    /** @return array{code: string, digest: string, expiresAt: \DateTimeImmutable} */
    public function createPairingCode(): array
    {
        $code = (string) random_int(100000, 999999);

        return [
            'code' => $code,
            'digest' => $this->pairingDigest($code),
            'expiresAt' => new \DateTimeImmutable(sprintf('+%d minutes', $this->devicePairingCodeTtlMinutes)),
        ];
    }

    public function pairingDigest(string $code): string
    {
        return hash_hmac('sha256', $code, $this->devicePairingPepper);
    }

    /** @return array{token: string, digest: string, expiresAt: \DateTimeImmutable} */
    public function createCredential(): array
    {
        $token = rtrim(strtr(base64_encode(random_bytes(32)), '+/', '-_'), '=');

        return [
            'token' => $token,
            'digest' => $this->credentialDigest($token),
            'expiresAt' => $this->nextCredentialExpiry(),
        ];
    }

    public function credentialDigest(string $token): string
    {
        return hash_hmac('sha256', $token, $this->deviceCredentialPepper);
    }

    public function nextCredentialExpiry(): \DateTimeImmutable
    {
        return new \DateTimeImmutable(sprintf('+%d days', $this->deviceSessionTtlDays));
    }

    public function shouldRefresh(Device $device, \DateTimeImmutable $now): bool
    {
        $expiresAt = $device->getCredentialExpiresAt();

        return null !== $expiresAt && $expiresAt < $now->modify(sprintf('+%d days', max(1, $this->deviceSessionTtlDays - 30)));
    }
}
