<?php

declare(strict_types=1);

namespace App\Http\Middleware;

use App\Domain\Cluster\Models\ClusterNode;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

/**
 * Slave node isteklerinde HMAC-SHA256 doğrulaması.
 *
 * İmzalanan: raw request body
 * Header:    X-Cluster-Signature: <sha256-hex>
 *            X-Cluster-Master:    <master_url>  (bilgi amaçlı)
 *
 * Doğrulama başarılı olursa request'e 'cluster_node' attribute'u eklenir.
 * Tüm node'ların secret'ları iterate edilir — O(n) fakat node sayısı küçük.
 * Replay koruması: ±5 dakika timestamp toleransı (X-Cluster-Timestamp header).
 */
class VerifyClusterHmac
{
    public function handle(Request $request, Closure $next): Response
    {
        $sig = $request->header('X-Cluster-Signature', '');
        $timestamp = $request->header('X-Cluster-Timestamp', '');
        $body = $request->getContent();

        if ($sig === '') {
            return response()->json(['error' => 'Eksik X-Cluster-Signature header.'], 401);
        }

        // Replay koruması (header opsiyonel — slave agent gönderirse kontrol edilir)
        if ($timestamp !== '' && abs(time() - (int) $timestamp) > 300) {
            return response()->json(['error' => 'Timestamp aralık dışı (±5 dk).'], 401);
        }

        // Hangi node'un secret'ı eşleşiyor bul
        $matchedNode = $this->findNode($body, $sig);

        if ($matchedNode === null) {
            return response()->json(['error' => 'Geçersiz cluster imzası.'], 401);
        }

        $request->attributes->set('cluster_node', $matchedNode);

        return $next($request);
    }

    private function findNode(string $body, string $sig): ?ClusterNode
    {
        $nodes = ClusterNode::whereNotNull('api_secret')
            ->where('status', '!=', 'decommissioned')
            ->whereNull('deleted_at')
            ->get(['id', 'api_secret', 'hostname', 'status']);

        foreach ($nodes as $node) {
            $expected = hash_hmac('sha256', $body, $node->api_secret);
            if (hash_equals($expected, $sig)) {
                return $node->fresh(); // full model
            }
        }

        return null;
    }
}
