<?php

namespace Gaco\SnapBi\Services\Signature;

use Gaco\SnapBi\Contracts\ConfigInterface;
use Gaco\SnapBi\Helpers\SnapBiHelper;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Date;

/**
 * Service Signature (Virtual Account endpoints - Symmetric HMAC SHA512)
 * 
 * Algorithm: HMAC SHA512
 * 
 * String to Sign:
 * HTTPMethod + ":" + EndpointUrl + ":" + AccessToken + ":" + Lowercase(HexEncode(SHA256(minify(RequestBody)))) + ":" + TimeStamp
 */
class Service
{
    protected ?ConfigInterface $config = null;

    public function __construct(?ConfigInterface $config = null)
    {
        $this->config = $config;
    }

    /**
     * Create symmetric signature for service endpoints
     * 
     * Process:
     * 1. stringToSign = HTTPMethod + ":" + EndpointUrl + ":" + AccessToken + ":" + BodyHash + ":" + TimeStamp
     * 2. signature = HMAC_SHA512(clientSecret, stringToSign)
     * 3. X-SIGNATURE = Base64.encode(signature)
     * 
     * @param JsonResponse $response Response object
     * @param string|null $httpMethod HTTP method (default: POST)
     * @param string|null $endpointUrl Endpoint URL
     * @param string|null $accessToken Access token (from Authorization header)
     * @return JsonResponse Response with X-SIGNATURE header added
     * @throws \InvalidArgumentException If client secret is not available
     */
    public function create(
        JsonResponse $response,
        ?string $httpMethod = 'POST',
        ?string $endpointUrl = null,
        ?string $accessToken = null
    ): JsonResponse {
        if (!$this->config) {
            throw new \InvalidArgumentException('Configuration is required for signature creation');
        }

        $clientSecret = $this->config->get('client_secret');
        if (!$clientSecret) {
            throw new \InvalidArgumentException('Client secret is required for signature creation');
        }

        // Use SnapBiHelper method which already implements the logic
        return SnapBiHelper::addResponseSignature(
            $response,
            $clientSecret,
            $httpMethod,
            $endpointUrl,
            $accessToken
        );
    }

    /**
     * Verify symmetric signature for service endpoints
     * 
     * Process:
     * 1. Extract headers and body from request
     * 2. Build stringToSign = HTTPMethod + ":" + EndpointUrl + ":" + AccessToken + ":" + BodyHash + ":" + TimeStamp
     * 3. Calculate expected signature = HMAC_SHA512(clientSecret, stringToSign)
     * 4. Compare with X-SIGNATURE header
     * 
     * @param Request $request HTTP request
     * @param string|null $endpointUrl Endpoint URL (optional, can be extracted from request)
     * @return bool True if signature is valid
     * @throws \InvalidArgumentException If required headers or client secret are missing
     */
    public function verify(Request $request, ?string $endpointUrl = null): bool
    {
        if (!$this->config) {
            throw new \InvalidArgumentException('Configuration is required for signature verification');
        }

        $clientSecret = $this->config->get('client_secret');
        if (!$clientSecret) {
            throw new \InvalidArgumentException('Client secret is required for signature verification');
        }

        // Get required headers
        $signature = $request->header('X-SIGNATURE');
        $timestamp = $request->header('X-TIMESTAMP');
        $accessToken = $request->header('Authorization');
        if ($accessToken) {
            // Remove "Bearer " prefix if present
            $accessToken = preg_replace('/^Bearer\s+/i', '', $accessToken);
        }

        if (!$signature || !$timestamp) {
            return false;
        }

        // Get HTTP method
        $httpMethod = $request->method();

        // Get endpoint URL
        if ($endpointUrl === null) {
            $endpointUrl = $request->fullUrl();
        }

        // Get request body
        $requestBody = $request->getContent();
        if (empty($requestBody)) {
            $requestBody = json_encode($request->all(), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
        }

        // Minify request body
        $minifiedBody = json_encode(json_decode($requestBody, true), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);

        // Calculate body hash: Lowercase(HexEncode(SHA256(minify(RequestBody))))
        $bodyHash = strtolower(bin2hex(hash('sha256', $minifiedBody, true)));

        // Build signature string
        $signatureString = sprintf(
            '%s:%s:%s:%s:%s',
            strtoupper($httpMethod),
            $endpointUrl,
            $accessToken ?? '',
            $bodyHash,
            $timestamp
        );

        // Decode client secret from Base64
        $clientSecretDecoded = base64_decode($clientSecret, true);
        if ($clientSecretDecoded === false) {
            return false;
        }

        // Calculate expected signature
        $expectedSignature = base64_encode(hash_hmac('sha512', $signatureString, $clientSecretDecoded, true));

        // Compare signatures (timing-safe comparison)
        return hash_equals($expectedSignature, $signature);
    }
}

