<?php

declare(strict_types=1);

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;

/**
 * EnforceTwoFactor — config('wdot.two_factor.enforce_for_roles') 에 속한 사용자가
 * 2FA 미설정 상태로 보호 라우트 접근 시 /2fa/setup 또는 401 로 라우팅.
 *
 * - 비인증 사용자는 통과 (다른 auth 미들웨어가 처리)
 * - 2FA 활성 사용자는 통과
 * - role 미해당 사용자는 통과
 * - API 요청 (Accept: application/json) 은 423 Locked + JSON 응답
 * - HTML 요청은 /2fa/setup 으로 redirect
 *
 * @see config/wdot.php two_factor
 */
final class EnforceTwoFactor
{
    public function handle(Request $request, Closure $next): SymfonyResponse
    {
        if (! config('wdot.two_factor.enforce_for_super_admin', true)) {
            return $next($request);
        }

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

        $roles = (array) config('wdot.two_factor.enforce_for_roles', ['super-admin']);
        $userHasGuardedRole = false;

        if (method_exists($user, 'hasAnyRole')) {
            $userHasGuardedRole = (bool) $user->hasAnyRole($roles);
        }

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

        $enabled = (bool) ($user->two_factor_enabled
            ?? ($user->two_factor_secret !== null && $user->two_factor_confirmed_at !== null));

        if ($enabled) {
            return $next($request);
        }

        // Skip the setup route itself + logout to avoid redirect loops.
        $path = $request->path();
        if (in_array($path, ['2fa/setup', '2fa/confirm', 'logout'], true)
            || str_starts_with($path, '2fa/')) {
            return $next($request);
        }

        if ($request->expectsJson() || $request->is('api/*')) {
            return response()->json([
                'error' => 'two_factor_required',
                'message' => '슈퍼관리자 권한에는 2단계 인증 설정이 필수입니다.',
                'redirect' => url('/2fa/setup'),
            ], Response::HTTP_LOCKED);
        }

        return redirect()->to('/2fa/setup');
    }
}
