<?php

namespace App\Services;

use App\Models\Members\User;
use Illuminate\Support\Facades\Log;

/**
 * 내부 서버간 토큰 교환 서비스
 *
 * MNG 서버에서 HMAC 서명된 페이로드를 검증하고
 * API 사용을 위한 Sanctum 토큰을 발급합니다.
 */
class InternalTokenService
{
    /**
     * 토큰 유효 시간 (초) - 기본 1시간
     */
    private const TOKEN_EXPIRES_IN = 3600;

    /**
     * 서명 유효 시간 (초) - 기본 5분
     */
    private const SIGNATURE_VALID_DURATION = 300;

    /**
     * 서명된 페이로드 검증
     *
     * @param  int  $userId  사용자 ID
     * @param  int  $tenantId  테넌트 ID
     * @param  int  $exp  만료 타임스탬프
     * @param  string  $signature  HMAC 서명
     * @return array{valid: bool, error?: string}
     */
    public function verifySignature(int $userId, int $tenantId, int $exp, string $signature): array
    {
        $sharedSecret = config('services.internal.exchange_secret');

        if (empty($sharedSecret)) {
            Log::error('[InternalTokenService] exchange_secret not configured');

            return ['valid' => false, 'error' => __('error.internal.secret_not_configured')];
        }

        // 만료 시간 검증 (현재 시간 기준 SIGNATURE_VALID_DURATION 초 이내)
        $now = time();
        if ($exp < $now) {
            Log::warning('[InternalTokenService] Signature expired', [
                'exp' => $exp,
                'now' => $now,
                'diff' => $now - $exp,
            ]);

            return ['valid' => false, 'error' => __('error.internal.signature_expired')];
        }

        if ($exp > $now + self::SIGNATURE_VALID_DURATION) {
            Log::warning('[InternalTokenService] Signature exp too far in future', [
                'exp' => $exp,
                'now' => $now,
                'diff' => $exp - $now,
            ]);

            return ['valid' => false, 'error' => __('error.internal.invalid_exp')];
        }

        // HMAC 서명 검증
        $payload = "{$userId}:{$tenantId}:{$exp}";
        $expectedSignature = hash_hmac('sha256', $payload, $sharedSecret);

        if (! hash_equals($expectedSignature, $signature)) {
            Log::warning('[InternalTokenService] Invalid signature', [
                'user_id' => $userId,
                'tenant_id' => $tenantId,
            ]);

            return ['valid' => false, 'error' => __('error.internal.invalid_signature')];
        }

        return ['valid' => true];
    }

    /**
     * 사용자 토큰 발급
     *
     * @param  int  $userId  사용자 ID
     * @param  int  $tenantId  테넌트 ID
     * @return array{access_token: string, token_type: string, expires_in: int}|null
     */
    public function issueToken(int $userId, int $tenantId): ?array
    {
        $user = User::find($userId);

        if (! $user) {
            Log::warning('[InternalTokenService] User not found', ['user_id' => $userId]);

            return null;
        }

        // 해당 테넌트에 소속되어 있는지 확인
        $userTenant = $user->userTenants()->where('tenant_id', $tenantId)->first();
        if (! $userTenant) {
            Log::warning('[InternalTokenService] User not in tenant', [
                'user_id' => $userId,
                'tenant_id' => $tenantId,
            ]);

            return null;
        }

        // 기존 mng_session 토큰 삭제 (중복 방지)
        $user->tokens()->where('name', 'mng_session')->delete();

        // 새 토큰 발급
        $token = $user->createToken('mng_session', ['*'], now()->addSeconds(self::TOKEN_EXPIRES_IN));

        Log::info('[InternalTokenService] Token issued', [
            'user_id' => $userId,
            'tenant_id' => $tenantId,
            'token_id' => $token->accessToken->id,
        ]);

        return [
            'access_token' => $token->plainTextToken,
            'token_type' => 'Bearer',
            'expires_in' => self::TOKEN_EXPIRES_IN,
        ];
    }

    /**
     * 토큰 교환 실행 (검증 + 발급)
     *
     * @param  int  $userId  사용자 ID
     * @param  int  $tenantId  테넌트 ID
     * @param  int  $exp  만료 타임스탬프
     * @param  string  $signature  HMAC 서명
     * @return array{success: bool, data?: array, error?: string}
     */
    public function exchange(int $userId, int $tenantId, int $exp, string $signature): array
    {
        // 1. 서명 검증
        $verification = $this->verifySignature($userId, $tenantId, $exp, $signature);
        if (! $verification['valid']) {
            return [
                'success' => false,
                'error' => $verification['error'],
            ];
        }

        // 2. 토큰 발급
        $tokenData = $this->issueToken($userId, $tenantId);
        if (! $tokenData) {
            return [
                'success' => false,
                'error' => __('error.internal.token_issue_failed'),
            ];
        }

        return [
            'success' => true,
            'data' => $tokenData,
        ];
    }
}
