<?php

namespace App\Http\Middleware;

use App\Services\AccessControl\DataScopeService;
use Closure;
use App\Services\Security\SecurityEventLogger;
use Illuminate\Support\Facades\Auth;

class EnforceNgbvdRoutePermissions
{
    public function handle($request, Closure $next)
    {
        $user = Auth::user();

        if (!$user) {
            return $next($request);
        }

        if (method_exists($user, 'canBypassNgbvdPermissions') && $user->canBypassNgbvdPermissions()) {
            return $next($request);
        }

        $permissions = $this->permissionsForRequest($request);
        if (!empty($permissions) && !$this->userHasAnyPermission($user, $permissions)) {
            return $this->deny(
                $request,
                'You do not have permission to access this resource.',
                $permissions
            );
        }

        if ($this->requiresCaseRecordScopeCheck($request)) {
            $caseId = $request->query('id') ?: $request->route('id');
            if ($caseId && !app(DataScopeService::class)->userCanAccessVictimReference($user, $caseId)) {
                return $this->deny(
                    $request,
                    'Your account is not authorised to access this case record under your assigned data scope.',
                    ['data_scope.case_record']
                );
            }
        }

        return $next($request);
    }

    private function permissionsForRequest($request): array
    {
        foreach (config('ngbvd_permissions.route_permissions', []) as $pattern => $permissions) {
            if ($request->is($pattern)) {
                return array_values(array_filter((array) $permissions));
            }
        }
        return [];
    }

    private function userHasAnyPermission($user, array $permissions): bool
    {
        foreach ($permissions as $permission) {
            if (method_exists($user, 'hasNgbvdPermission') && $user->hasNgbvdPermission($permission)) {
                return true;
            }
        }
        return false;
    }

    private function requiresCaseRecordScopeCheck($request): bool
    {
        foreach (config('ngbvd_permissions.record_scope_routes', []) as $pattern) {
            if ($request->is($pattern)) {
                return true;
            }
        }
        return false;
    }

    private function deny($request, string $message = 'You do not have permission to access this resource.', array $permissions = [])
    {
        app(SecurityEventLogger::class)->log('authorization_denied', 'warning', ['required_permissions' => $permissions, 'message' => $message], $request);
        $requestedResource = '/' . ltrim($request->path(), '/');
        $requestedAction = !empty($permissions) ? implode(', ', $permissions) : 'restricted resource access';

        if ($request->expectsJson() || $request->ajax()) {
            return response()->json([
                'success' => false,
                'message' => $message,
                'requested_resource' => $requestedResource,
                'required_permission' => $requestedAction,
            ], 403);
        }

        return redirect('/error/access')
            ->with('error', $message)
            ->with('access_denied_resource', $requestedResource)
            ->with('access_denied_action', $requestedAction)
            ->with('access_denied_message', $message);
    }
}
