<?php

namespace App\Http\Middleware;

use App\Support\ApiError;
use Closure;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpFoundation\Response;

class TenantIsolation
{
    /**
     * Handle an incoming request.
     */
    public function handle(Request $request, Closure $next)
    {
        $tenantId = $request->attributes->get('tenant_id');

        $abilities = $request->user()?->currentAccessToken()?->abilities ?? [];

        if (! $tenantId && in_array('super-admin', $abilities, true)) {
            return $next($request);
        }

        if (! $tenantId) {
            return $this->missingTenantIdResponse();
        }

        // Get the tenant from request (query param, route param, or header)
        $requestTenantId = $this->getTenantIdFromRequest($request);

        if ($requestTenantId && $requestTenantId != $tenantId) {
            return $this->tenantIsolationViolationResponse((int) $tenantId, (int) $requestTenantId);
        }

        // Set tenant context for query scoping
        $connection = DB::connection();
        if (in_array($connection->getDriverName(), ['mysql', 'mariadb'])) {
            $connection->statement('SET @tenant_id = ?', [$tenantId]);
        }

        // Add tenant context to request for easy access in controllers
        $request->attributes->set('current_tenant_id', $tenantId);

        return $next($request);
    }

    /**
     * Extract tenant ID from request
     */
    private function getTenantIdFromRequest(Request $request): ?int
    {
        // 1. Route parameter (e.g., /api/v1/tenant/123/events)
        if ($request->route('tenant')) {
            return (int) $request->route('tenant');
        }

        // 2. Query parameter (e.g., ?tenant_id=123)
        if ($request->query('tenant_id')) {
            return (int) $request->query('tenant_id');
        }

        // 3. Header (X-Tenant-ID)
        if ($request->header('X-Tenant-ID')) {
            return (int) $request->header('X-Tenant-ID');
        }

        // 4. For tenant-specific resources, use token tenant_id
        return null;
    }

    private function missingTenantIdResponse(): JsonResponse
    {
        return ApiError::response(
            'tenant_context_missing',
            'Tenant Context Missing',
            'Tenant ID not found in access token.',
            Response::HTTP_UNAUTHORIZED
        );
    }

    private function tenantIsolationViolationResponse(int $tokenTenantId, int $requestTenantId): JsonResponse
    {
        return ApiError::response(
            'tenant_isolation_violation',
            'Tenant Isolation Violation',
            'The requested resource belongs to a different tenant.',
            Response::HTTP_FORBIDDEN,
            [
                'token_tenant_id' => $tokenTenantId,
                'request_tenant_id' => $requestTenantId,
            ]
        );
    }
}
