<?php

namespace App\Http\Middleware;

use Closure;
use App\Models\AdminUser;
use App\Lottery\ErrorCode;
use App\Support\ApiMessage;
use App\Support\ApiResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpFoundation\Response;

final class EnsureAdminApiResourcePermission
{
    public function handle(Request $request, Closure $next): Response
    {
        $admin = $request->lotteryAdmin();
        if (! $admin instanceof AdminUser) {
            return ApiResponse::error(
                trans('admin.unauthenticated', [], $request->lotteryLocale()),
                ErrorCode::AdminUnauthenticated->value,
                null,
                401,
            );
        }

        $route = $request->route();
        $routeName = is_object($route) ? $route->getName() : null;
        if (! is_string($routeName) || $routeName === '') {
            return ApiMessage::errorResponse($request, 'admin.route_name_missing_for_permission', ErrorCode::InternalError->value, null, 500);
        }

        $normalizedRouteName = $this->normalizeAdminRouteName($routeName);
        $resource = DB::table('admin_api_resources')
            ->where('route_name', $normalizedRouteName)
            ->where('status', 1)
            ->first(['id', 'code', 'auth_mode']);

        if ($resource === null) {
            return ApiMessage::errorResponse(
                $request,
                'admin.api_resource_not_configured',
                ErrorCode::InternalError->value,
                ['route_name' => $normalizedRouteName],
                500,
                ['route' => $normalizedRouteName],
            );
        }

        if ($resource->auth_mode === 'login_only') {
            return $next($request);
        }

        $permissionCodes = DB::table('admin_api_resource_bindings as arb')
            ->join('admin_menu_actions as ma', 'ma.id', '=', 'arb.menu_action_id')
            ->where('arb.api_resource_id', (int) $resource->id)
            ->where('ma.status', 1)
            ->pluck('ma.permission_code')
            ->filter(static fn ($code): bool => is_string($code) && $code !== '')
            ->values()
            ->all();

        if ($permissionCodes === []) {
            if ($admin->isSuperAdmin()) {
                return $next($request);
            }

            return ApiMessage::errorResponse(
                $request,
                'admin.api_resource_no_permission_binding',
                ErrorCode::InternalError->value,
                ['resource_code' => $resource->code],
                500,
                ['code' => (string) $resource->code],
            );
        }

        foreach ($permissionCodes as $permissionCode) {
            if ($admin->hasAdminPermission((string) $permissionCode)) {
                return $next($request);
            }
        }

        return ApiResponse::error(
            trans('admin.permission_denied', [], $request->lotteryLocale()),
            ErrorCode::AdminForbidden->value,
            ['required_any_codes' => $permissionCodes, 'resource_code' => $resource->code],
            403,
        );
    }

    private function normalizeAdminRouteName(string $routeName): string
    {
        return preg_replace('/^(api\.v1\.admin\.)+/', 'api.v1.admin.', $routeName) ?? $routeName;
    }
}
