<?php

namespace App\Http\Responses;

use App\Http\Controllers\InviteController;
use App\Models\Admin;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Laravel\Fortify\Contracts\LoginResponse as LoginResponseContract;
use Symfony\Component\HttpFoundation\Response;

class LoginResponse implements LoginResponseContract
{
    public function toResponse($request): Response
    {
        $user = auth()->user();

        // ── 2FA Challenge Interception ──────────────────────────────

        // If the user has 2FA enabled, log them out immediately and
        // redirect to the 2FA challenge page with their ID in session.
        if ($user->hasTwoFactorEnabled()) {
            $userId   = $user->id;
            $remember = $request->boolean('remember');

            // Log the user out so they are not authenticated until 2FA passes
            Auth::logout();

            // Store just enough to complete login after 2FA verification
            $request->session()->put('2fa:user_id', $userId);
            $request->session()->put('2fa:remember', $remember);

            // Preserve the intended URL so we can redirect after 2FA
            if ($request->session()->has('url.intended')) {
                $request->session()->put('2fa:intended', $request->session()->get('url.intended'));
            }

            if ($request->wantsJson()) {
                return response()->json(['two_factor' => true]);
            }

            return redirect()->route('two-factor.challenge');
        }

        // ── Standard login flow ─────────────────────────────────────

        \Illuminate\Support\Facades\Log::info('LoginResponse::toResponse standard login flow', [
            'user_id'    => $user?->id,
            'user_email' => $user?->email,
            'session_id' => session()->getId(),
            'has_pending_invite_token' => session()->has('pending_invite_token'),
        ]);

        // Process any pending workspace invitation from session
        $inviteRedirect = InviteController::processPendingInvite($user);
        if ($inviteRedirect) {
            \Illuminate\Support\Facades\Log::info('LoginResponse: invite consumed, redirecting via invite path');
            return $inviteRedirect;
        }

        if ($user->is_admin) {
            $home = url('/admin/dashboard');
            $homePicked = 'is_admin branch -> /admin/dashboard';
        } elseif (!$user->hasWorkspace()) {
            $home = url('/onboarding/step-1');
            $homePicked = 'no-workspace branch -> /onboarding/step-1';
        } else {
            // Auto-complete onboarding if workspace exists but not marked complete
            if ($user->activeWorkspace && !$user->activeWorkspace->onboarding_completed) {
                $user->activeWorkspace->update(['onboarding_completed' => true, 'onboarding_step' => 5]);
            }
            $home = url('/dashboard');
            $homePicked = 'workspace branch -> /dashboard';
        }

        Log::info('LoginResponse: home picked', [
            'user_id'       => $user->id,
            'is_admin'      => $user->is_admin,
            'admin_role'    => $user->admin_role,
            'has_workspace' => $user->hasWorkspace(),
            'home_picked'   => $homePicked,
            'home_url'      => $home,
        ]);

        // ── Admin guard bridge ───────────────────────────────────────
        // The codebase has TWO auth guards:
        //   - 'web'   → users table  (Fortify logs you in here)
        //   - 'admin' → admins table (the /admin/* routes require this)
        // Per app/Models/Admin.php, "authentication within [the admin]
        // area relies on this Admin model and the 'admin' guard."
        //
        // When a user with is_admin=true logs in via Fortify they get a
        // 'web' session but NOT an 'admin' session. Visiting /admin/*
        // then redirects to /login (auth:admin rejects), Fortify sees
        // 'web' is still authenticated and bounces them to /dashboard —
        // the redirect loop the customer hit.
        //
        // Bridge: if the freshly-logged-in user is_admin, find or create
        // the matching Admin record (same email, copied password hash)
        // and log them into the 'admin' guard too. After this, /admin/*
        // works on the very next request.
        if ($user->is_admin) {
            try {
                $admin = Admin::firstOrCreate(
                    ['email' => strtolower($user->email)],
                    [
                        'name'     => $user->name,
                        'password' => $user->password, // already hashed
                        'role'     => $user->admin_role ?? 'super_admin',
                    ]
                );
                Auth::guard('admin')->login($admin, true);
                Log::info('LoginResponse: bridged web -> admin guard', [
                    'user_id'    => $user->id,
                    'admin_id'   => $admin->id,
                    'admin_role' => $admin->role,
                    'created'    => $admin->wasRecentlyCreated,
                ]);
            } catch (\Throwable $e) {
                Log::warning('LoginResponse: admin guard bridge failed', [
                    'user_id' => $user->id,
                    'error'   => $e->getMessage(),
                ]);
            }
        }

        // FIX-121: Use explicit redirect instead of intended() to prevent open redirect
        if ($request->wantsJson()) {
            return response()->json(['two_factor' => false]);
        }

        // Only allow intended redirect to internal paths
        $rawIntended = session()->get('url.intended');
        $intended = session()->pull('url.intended', $home);
        if (!str_starts_with($intended, '/') || str_starts_with($intended, '//')) {
            $intended = $home;
        }

        // ── Admin lock-in: never honour a non-admin intended URL for
        // an admin user. Without this, a stale url.intended=/dashboard
        // from an earlier visit would override $home and dump them on
        // the user dashboard right after login. Extract the path
        // portion before testing the /admin prefix — $intended can be
        // an absolute URL whose leading characters are "https://".
        $intendedPath = parse_url($intended, PHP_URL_PATH) ?: $intended;
        if ($user->is_admin && ! str_starts_with($intendedPath, '/admin')) {
            Log::info('LoginResponse: admin lock-in — discarding non-admin intended URL', [
                'user_id'        => $user->id,
                'discarded'      => $intended,
                'discarded_path' => $intendedPath,
                'forced_target'  => $home,
            ]);
            $intended = $home;
        }

        Log::info('LoginResponse: final redirect decision', [
            'user_id'        => $user->id,
            'raw_intended'   => $rawIntended,
            'final_intended' => $intended,
            'home_fallback'  => $home,
            'used_intended'  => $rawIntended !== null && $intended !== $home,
        ]);

        return redirect($intended);
    }
}
