<?php

namespace App\Services\Staff;

class StaffTimeOffLinkService
{
    private const TOKEN_CONTEXT = 'timeoff_notify';
    private const DEFAULT_TTL = 1209600;

    private string $secret;
    private int $ttl;

    public function __construct(?string $secret = null, ?int $ttlSeconds = null)
    {
        $this->secret = $secret ?: (string) env('JWT_SECRET', 'change-me-in-env');
        $this->ttl = ($ttlSeconds !== null && $ttlSeconds > 0) ? $ttlSeconds : self::DEFAULT_TTL;
    }

    public function createToken(array $payload): string
    {
        $now = time();
        $data = array_merge($payload, [
            'iat' => $now,
            'exp' => $now + $this->ttl,
            'ctx' => self::TOKEN_CONTEXT,
        ]);

        return $this->jwtEncode($data, $this->secret);
    }

    public function parseToken(?string $token): ?array
    {
        if (!is_string($token) || $token === '') {
            return null;
        }

        $payload = $this->jwtDecode($token, $this->secret);
        if (!is_array($payload)) {
            return null;
        }

        if (($payload['ctx'] ?? null) !== self::TOKEN_CONTEXT) {
            return null;
        }

        $exp = (int) ($payload['exp'] ?? 0);
        if ($exp > 0 && $exp < time()) {
            return null;
        }

        return $payload;
    }

    private function jwtEncode(array $payload, string $secret): string
    {
        $header = ['typ' => 'JWT', 'alg' => 'HS256'];
        $segments = [];
        $segments[] = $this->base64UrlEncode(json_encode($header, JSON_UNESCAPED_SLASHES));
        $segments[] = $this->base64UrlEncode(json_encode($payload, JSON_UNESCAPED_SLASHES));
        $signingInput = implode('.', $segments);

        $signature = hash_hmac('sha256', $signingInput, $secret, true);
        $segments[] = $this->base64UrlEncode($signature);

        return implode('.', $segments);
    }

    private function jwtDecode(string $token, string $secret): ?array
    {
        $parts = explode('.', $token);
        if (count($parts) !== 3) {
            return null;
        }

        [$header64, $payload64, $signature64] = $parts;
        $header = json_decode($this->base64UrlDecode($header64), true);
        $payload = json_decode($this->base64UrlDecode($payload64), true);
        $signature = $this->base64UrlDecode($signature64);

        if (!is_array($header) || !is_array($payload)) {
            return null;
        }

        $signingInput = $header64 . '.' . $payload64;
        if (($header['alg'] ?? '') !== 'HS256') {
            return null;
        }

        $expected = hash_hmac('sha256', $signingInput, $secret, true);
        if (!hash_equals($expected, $signature)) {
            return null;
        }

        return $payload;
    }

    private function base64UrlEncode(string $data): string
    {
        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
    }

    private function base64UrlDecode(string $data): string
    {
        $padding = strlen($data) % 4;
        if ($padding > 0) {
            $data .= str_repeat('=', 4 - $padding);
        }
        return base64_decode(strtr($data, '-_', '+/')) ?: '';
    }
}
