<?php

namespace App\Services;

class AgoraTokenService
{
    private const VERSION   = '006';
    private const PRIVILEGE_JOIN_CHANNEL = 1;
    private const PRIVILEGE_PUBLISH_AUDIO_STREAM = 2;
    private const PRIVILEGE_PUBLISH_VIDEO_STREAM = 3;
    private const PRIVILEGE_PUBLISH_DATA_STREAM  = 4;

    public static function generateToken(string $channelName, int $uid, int $expireSeconds = 3600): string
    {
        $appId          = config('services.agora.app_id', env('AGORA_APP_ID', ''));
        $appCertificate = config('services.agora.app_certificate', env('AGORA_APP_CERTIFICATE', ''));

        if (!$appId || !$appCertificate) {
            // Development fallback — no token required when cert is empty
            return '';
        }

        $currentTimestamp = time();
        $expireTimestamp  = $currentTimestamp + $expireSeconds;

        $nonce = random_int(1, 16777216);

        $privileges = [
            self::PRIVILEGE_JOIN_CHANNEL          => $expireTimestamp,
            self::PRIVILEGE_PUBLISH_AUDIO_STREAM  => $expireTimestamp,
            self::PRIVILEGE_PUBLISH_VIDEO_STREAM  => 0,
            self::PRIVILEGE_PUBLISH_DATA_STREAM   => $expireTimestamp,
        ];

        // Pack message
        $message = self::packUint16(1);                    // version: 1 (AccessToken)
        $message .= self::packUint32($currentTimestamp);
        $message .= self::packUint32($nonce);
        $message .= self::packString($channelName);
        $message .= self::packUint32($uid);
        $message .= self::packPrivileges($privileges);

        // HMAC-SHA256 signature
        $signature = hash_hmac('sha256', $appId . $currentTimestamp . $nonce . $channelName . $uid . self::packPrivileges($privileges), $appCertificate, true);

        $content = self::packString($signature) . $message;

        return self::VERSION . $appId . base64_encode($content);
    }

    private static function packUint16(int $v): string
    {
        return pack('n', $v);
    }

    private static function packUint32(int $v): string
    {
        return pack('N', $v);
    }

    private static function packString(string $v): string
    {
        return pack('n', strlen($v)) . $v;
    }

    private static function packPrivileges(array $privileges): string
    {
        ksort($privileges);
        $packed = pack('n', count($privileges));
        foreach ($privileges as $key => $value) {
            $packed .= pack('n', $key) . pack('N', $value);
        }
        return $packed;
    }
}
