<?php

namespace App\Http\Controllers\Api\Auth;

use App\Http\Controllers\Controller;
use App\Http\Requests\Auth\LoginSessionRequest;
use App\Http\Requests\Auth\SetRoleSessionRequest;
use App\Models\User;
use App\Services\ApplicationUrlService;
use App\Services\Auth\ApiLoginSecurityService;
use App\Services\Auth\AuthSessionService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use PHPOpenSourceSaver\JWTAuth\Facades\JWTAuth;

/**
 * Session (cookie) endpoints for SPA — JSON alongside JWT on /api/login.
 *
 * Routes: GET login, POST user/login, GET logout, GET select-role, POST set-role (registered on web middleware).
 */
class AuthSessionController extends Controller
{
    public function __construct(
        private ApiLoginSecurityService $security,
        private AuthSessionService $sessionAuth,
        private ApplicationUrlService $urls,
    ) {}

    /** Returns login bootstrap state for the web portal. */
    public function loginMask(Request $request): JsonResponse
    {
        $redirectTo = $this->sessionAuth->sanitizeRedirectTarget(
            (string) ($request->query('redirect_to') ?? ''),
        );

        if (! Auth::guard('web')->check()) {
            return response()->json([
                'status' => true,
                'authenticated' => false,
                'redirect_to' => $redirectTo,
            ]);
        }

        /** @var list<string>|null $roles */
        $roles = session('roles');
        $roles = is_array($roles) ? array_values(array_filter(array_map('strval', $roles))) : [];
        $currentRole = session('role');

        if ($roles !== [] && count($roles) > 1 && ! $currentRole) {
            return response()->json([
                'status' => true,
                'authenticated' => true,
                'next_url' => $this->urls->webSelectRoleUrl(false),
                'roles' => $roles,
                'pending_redirect' => session('post_login_redirect'),
            ]);
        }

        if ($redirectTo !== null) {
            return response()->json([
                'status' => true,
                'authenticated' => true,
                'next_url' => $redirectTo,
            ]);
        }

        $pick = $currentRole !== null && $currentRole !== ''
            ? [(string) $currentRole]
            : $roles;

        return response()->json([
            'status' => true,
            'authenticated' => true,
            'next_url' => $this->sessionAuth->dashboardRouteForRoles($pick),
        ]);
    }

    /** Creates a web session from the submitted credentials. */
    public function login(LoginSessionRequest $request): JsonResponse
    {
        $email = (string) $request->validated('email');
        $password = (string) $request->validated('password');
        $redirectRaw = $request->validated('redirect_to');
        $sanitizedRedirect = $redirectRaw !== null
            ? $this->sessionAuth->sanitizeRedirectTarget((string) $redirectRaw)
            : null;

        $ip = (string) $request->ip();

        if ($this->security->isIpBlocked($ip)) {
            return response()->json([
                'status' => false,
                'message' => 'Too many failed attempts from your IP. Please try again later.',
            ], 429);
        }

        $user = User::query()->where('email', $email)->first();

        if (! $user) {
            $this->security->logIpAttempt($ip);

            return response()->json([
                'status' => false,
                'message' => 'The email and password combination you entered is invalid. Please try again.',
            ], 401);
        }

        if ($user->is_suspended) {
            return response()->json([
                'status' => false,
                'message' => 'Account suspended. Please check your email to reset your password.',
            ], 403);
        }

        if (! verify_stored_password($password, (string) $user->password)) {
            $this->security->handleFailedLogin($user, $email, $ip);

            return response()->json([
                'status' => false,
                'message' => 'The email and password combination you entered is invalid. Please try again.',
            ], 401);
        }

        $fresh = $user->fresh();
        if (! $fresh) {
            return response()->json([
                'status' => false,
                'message' => 'The email and password combination you entered is invalid. Please try again.',
            ], 401);
        }

        $this->security->resetFailedAttempts($fresh);
        $this->security->logSuccessfulLogin($fresh, $request);

        $dest = $this->sessionAuth->resolvePostLoginDestination($fresh, $sanitizedRedirect);

        if (($dest['kind'] ?? '') === 'no_roles') {
            return response()->json([
                'status' => false,
                'message' => (string) ($dest['reason'] ?? 'Role not assigned. Please contact support.'),
            ], 403);
        }

        $loginPayload = $this->security->buildLoginResponse($fresh, JWTAuth::fromUser($fresh));

        return response()->json(array_merge($loginPayload, [
            'requires_role_selection' => ($dest['kind'] ?? '') === 'select_role',
            'roles' => $dest['roles'] ?? null,
            'next_url' => $dest['redirect_url'] ?? $this->urls->docsHomeUrl(false),
        ]));
    }

    /** Closes the current web session. */
    public function logout(Request $request): JsonResponse
    {
        $userId = Auth::guard('web')->id();
        $email = Auth::guard('web')->user()?->email ?? session('user_email');

        $this->sessionAuth->logLogout($userId ? (int) $userId : null, $email ? (string) $email : null);

        Auth::guard('web')->logout();

        $request->session()->invalidate();
        $request->session()->regenerateToken();

        return response()->json([
            'status' => true,
            'next_url' => $this->urls->docsHomeUrl(false),
        ]);
    }

    /** Returns the available post-login roles for the current user. */
    public function selectRole(Request $request): JsonResponse
    {
        if (! Auth::guard('web')->check()) {
            return response()->json([
                'status' => false,
                'message' => 'No roles available.',
            ], 401);
        }

        /** @var list<string>|null $roles */
        $roles = session('roles');
        $roles = is_array($roles) ? array_values(array_filter(array_map('strval', $roles))) : [];

        if ($roles === []) {
            return response()->json([
                'status' => false,
                'message' => 'No roles available.',
            ], 422);
        }

        return response()->json([
            'status' => true,
            'roles' => $roles,
            'pending_redirect' => session('post_login_redirect'),
            'redirect_to_query' => $this->sessionAuth->sanitizeRedirectTarget(
                (string) ($request->query('redirect_to') ?? ''),
            ),
        ]);
    }

    /** Stores the selected post-login role in session state. */
    public function setRole(SetRoleSessionRequest $request): JsonResponse
    {
        if (! Auth::guard('web')->check()) {
            return response()->json([
                'status' => false,
                'message' => 'Unauthorized.',
            ], 401);
        }

        $selectedRole = (string) $request->validated('selected_role');

        /** @var list<string>|null $available */
        $available = session('roles');
        $available = is_array($available) ? array_values(array_filter(array_map('strval', $available))) : [];

        if ($available === [] || ! in_array($selectedRole, $available, true)) {
            return response()->json([
                'status' => false,
                'message' => 'Invalid role selected.',
            ], 422);
        }

        $redirectRaw = $request->validated('redirect_to');
        $redirectTo = $redirectRaw !== null
            ? $this->sessionAuth->sanitizeRedirectTarget((string) $redirectRaw)
            : null;

        if ($redirectTo === null) {
            $redirectTo = $this->sessionAuth->sanitizeRedirectTarget(
                (string) (session('post_login_redirect') ?? ''),
            );
        }

        session(['role' => $selectedRole]);
        session()->forget('post_login_redirect');

        $next = $redirectTo ?? $this->sessionAuth->dashboardRouteForRoles([$selectedRole]);

        return response()->json([
            'status' => true,
            'role' => $selectedRole,
            'next_url' => $next,
        ]);
    }
}
