<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Models\SocialAccount;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use Laravel\Socialite\Facades\Socialite;

class SocialLoginController extends Controller
{
    public function redirect(string $provider)
    {
        // Defence-in-depth: even if the route guard is bypassed, refuse
        // to initiate the OAuth flow when admin disabled social login.
        if (\App\Models\SystemSetting::get('social_login_enabled', 'true') !== 'true') {
            return redirect('/login')->with('error', __('Social login is currently disabled.'));
        }

        // Pre-flight credential check. Without this, an empty
        // GOOGLE_CLIENT_ID / MICROSOFT_CLIENT_ID makes Socialite throw an
        // uncaught exception that bubbles up as a generic 500 page on
        // production (where APP_DEBUG=false hides the underlying message).
        // Customers reported seeing only "Something went wrong" with no
        // clear next step — that's the symptom this guard fixes.
        if (!$this->isProviderConfigured($provider)) {
            return redirect('/login')->with('error', __(
                ucfirst($provider) . ' sign-in is not configured. The administrator must add the OAuth Client ID and Client Secret.'
            ));
        }

        try {
            // Force the redirect URL to the SIGN-IN callback path. We can't
            // rely on config('services.{provider}.redirect') because the
            // same Google/Microsoft OAuth app is also used for connecting
            // mailboxes (email-oauth flow) which sets its own redirect URI
            // via .env (GOOGLE_REDIRECT_URI). Without this override,
            // Socialite would use the mailbox-connect URL for the sign-in
            // round trip and break the callback.
            return Socialite::driver($provider)
                ->redirectUrl($this->signInRedirectUrl($provider))
                ->redirect();
        } catch (\Throwable $e) {
            \Illuminate\Support\Facades\Log::error('Social login redirect failed', [
                'provider' => $provider,
                'error'    => $e->getMessage(),
            ]);
            return redirect('/login')->with('error', __(
                'Could not start ' . ucfirst($provider) . ' sign-in. Please try again or contact support.'
            ));
        }
    }

    /**
     * True when the provider has both a client id and client secret set
     * (in config/services.php, which is itself env- and settings-fed).
     * Used as a 422-style guard so missing config produces a polite
     * redirect-with-flash instead of a 500.
     */
    private function isProviderConfigured(string $provider): bool
    {
        $cfg = config("services.{$provider}");
        return !empty($cfg['client_id']) && !empty($cfg['client_secret']);
    }

    /**
     * Absolute URL for the SIGN-IN OAuth callback, decoupled from the
     * email-OAuth (mailbox connect) redirect URL that lives under
     * /email-oauth/{provider}/callback. Both share the same Google /
     * Microsoft app credentials, so we MUST pass distinct redirect URIs
     * per flow — otherwise Socialite uses whichever one .env wrote into
     * config('services.{provider}.redirect') and the wrong callback
     * fires (or Google rejects the code exchange).
     */
    private function signInRedirectUrl(string $provider): string
    {
        // Derive the callback URL from the CURRENT request, not APP_URL.
        //
        // Why: customers run MailTrixy on domains that resolve under both
        // `www.` and non-`www.` forms. With a hardcoded APP_URL, half the
        // visitors hit Google with a redirect_uri the OAuth client wasn't
        // registered for, getting `redirect_uri_mismatch` no matter
        // which of the two URIs the admin pastes into Google Console.
        //
        // url() resolves against the current request's scheme + host, so:
        //   - User on https://ciripit.ro/login     → ciripit.ro/auth/...callback
        //   - User on https://www.ciripit.ro/login → www.ciripit.ro/auth/...callback
        //
        // The seller just needs to register BOTH URIs (with and without
        // www) in Google Console once; users see the right one no matter
        // how they reached the login page.
        $url = url("/auth/{$provider}/callback");

        // Log it so admins can diagnose mismatches by tailing
        // storage/logs/laravel.log alongside their Google Console config.
        \Illuminate\Support\Facades\Log::info('Social login redirect URI', [
            'provider' => $provider,
            'url'      => $url,
        ]);

        return $url;
    }

    public function callback(string $provider)
    {
        if (!$this->isProviderConfigured($provider)) {
            return redirect(url('/login'))->with('error', __(
                ucfirst($provider) . ' sign-in is not configured.'
            ));
        }

        // Whitelist allowed providers to prevent XSS via flash message
        $allowedProviders = ['google', 'microsoft', 'github', 'facebook'];
        $safeProviderName = in_array($provider, $allowedProviders, true)
            ? ucfirst($provider)
            : 'the selected provider';

        try {
            // Must mirror the override done in redirect() above. Google /
            // Microsoft check the redirect URI on the code-exchange step —
            // if it doesn't match what was used to initiate the request,
            // the exchange fails with invalid_grant.
            $socialUser = Socialite::driver($provider)
                ->redirectUrl($this->signInRedirectUrl($provider))
                ->user();
        } catch (\Exception $e) {
            \Illuminate\Support\Facades\Log::error('Social login callback failed', [
                'provider' => $provider,
                'error'    => $e->getMessage(),
            ]);
            return redirect(url('/login'))->with('error', 'Unable to login with ' . $safeProviderName . '. Please try again.');
        }

        // Validate that the OAuth provider returned a valid access token
        if (!$socialUser->token) {
            return redirect(url('/login'))->with('error', 'Authentication failed — no access token received.');
        }

        // Find existing social account
        $socialAccount = SocialAccount::where('provider', $provider)
            ->where('provider_id', $socialUser->getId())
            ->first();

        if ($socialAccount) {
            $existingUser = $socialAccount->user;

            // FIX-012: Check account status before login
            if ($existingUser->status !== 'active') {
                return redirect(url('/login'))->with('error', 'Your account has been suspended or deactivated.');
            }

            // Update tokens
            $socialAccount->update([
                'token' => $socialUser->token,
                'refresh_token' => $socialUser->refreshToken,
                'token_expires_at' => $socialUser->expiresIn ? now()->addSeconds($socialUser->expiresIn) : null,
            ]);

            // FIX-008: Regenerate session BEFORE login to prevent session fixation
            session()->regenerate();
            Auth::login($existingUser);
            return redirect(url('/dashboard'));
        }

        // FIX-011: Normalize email to lowercase for case-insensitive lookup
        $email = strtolower($socialUser->getEmail());

        if (!$email) {
            return redirect(url('/login'))->with('error', 'No email address received from ' . $safeProviderName . '.');
        }

        // Find user by email or create new
        $user = User::whereRaw('LOWER(email) = ?', [$email])->first();

        if (!$user) {
            // FIX-009: Never trust provider's email_verified claim — always require verification
            $user = User::create([
                'name' => $socialUser->getName() ?? $socialUser->getNickname() ?? 'User',
                'email' => $email,
                'email_verified_at' => null, // Always require email verification
                'password' => null,
            ]);
        } else {
            // FIX-010/012: Verify existing user is active before linking social account
            if ($user->status !== 'active') {
                return redirect(url('/login'))->with('error', 'Your account has been suspended or deactivated.');
            }
        }

        // Create social account link
        $user->socialAccounts()->create([
            'provider' => $provider,
            'provider_id' => $socialUser->getId(),
            'token' => $socialUser->token,
            'refresh_token' => $socialUser->refreshToken,
            'token_expires_at' => $socialUser->expiresIn ? now()->addSeconds($socialUser->expiresIn) : null,
        ]);

        // FIX-008: Regenerate session BEFORE login to prevent session fixation
        session()->regenerate();
        Auth::login($user);

        // FIX-009: Send verification email if not yet verified
        if (!$user->hasVerifiedEmail()) {
            $user->sendEmailVerificationNotification();
        }

        if ($user->is_admin) {
            return redirect(url('/admin/dashboard'));
        }

        if (!$user->hasWorkspace()) {
            return redirect(url('/onboarding/step-1'));
        }

        return redirect(url('/dashboard'));
    }
}
