<?php

namespace Tests\Feature\Auth;

use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Hash;
use Laravel\Socialite\Facades\Socialite;
use Laravel\Socialite\Two\InvalidStateException;
use Laravel\Socialite\Two\User as SocialiteUser;
use Mockery;
use Tests\TestCase;

class GoogleAuthWorkflowRedirectTest extends TestCase
{
    use RefreshDatabase;

    public function test_google_callback_redirects_new_users_to_questionnaire(): void
    {
        $this->mockGoogleUser('Candidate@example.com');

        $response = $this->get(route('auth.google.callback'));

        $response->assertRedirect(route('questionnaire.show'));
        $this->assertAuthenticated();
        $this->assertDatabaseHas('users', [
            'email' => 'candidate@example.com',
            'google_id' => 'google-123',
            'role' => 'user',
        ]);
        $this->assertNotEmpty(User::query()->where('email', 'candidate@example.com')->value('password'));
    }

    public function test_google_callback_does_not_reset_existing_user_password(): void
    {
        User::factory()->create([
            'email' => 'candidate@example.com',
            'password' => Hash::make('ExistingPassword#2026'),
        ]);
        $this->mockGoogleUser('candidate@example.com');

        $response = $this->get(route('auth.google.callback'));

        $response->assertRedirect(route('questionnaire.show'));

        $user = User::query()->where('email', 'candidate@example.com')->firstOrFail();

        $this->assertTrue(Hash::check('ExistingPassword#2026', $user->password));
    }

    public function test_google_callback_redirects_questionnaire_completed_users_to_checkout(): void
    {
        User::factory()->create([
            'email' => 'candidate@example.com',
            'questionnaire_completed_at' => now(),
            'access_unlocked_at' => null,
        ]);
        $this->mockGoogleUser('candidate@example.com');

        $response = $this->get(route('auth.google.callback'));

        $response->assertRedirect(route('payment.checkout'));
    }

    public function test_google_callback_redirects_paid_users_without_analysis_to_resume_upload(): void
    {
        User::factory()->create([
            'email' => 'candidate@example.com',
            'questionnaire_completed_at' => now(),
            'access_unlocked_at' => now(),
            'resume_analyzed_at' => null,
        ]);
        $this->mockGoogleUser('candidate@example.com');

        $response = $this->get(route('auth.google.callback'));

        $response->assertRedirect(route('resume.show'));
    }

    public function test_google_callback_redirects_analyzed_users_without_profile_to_profile(): void
    {
        User::factory()->create([
            'email' => 'candidate@example.com',
            'questionnaire_completed_at' => now(),
            'access_unlocked_at' => now(),
            'resume_analyzed_at' => now(),
            'profile_completed_at' => null,
        ]);
        $this->mockGoogleUser('candidate@example.com');

        $response = $this->get(route('auth.google.callback'));

        $response->assertRedirect(route('profile.edit'));
    }

    public function test_google_callback_redirects_completed_users_to_applicant_dashboard(): void
    {
        User::factory()->create([
            'email' => 'candidate@example.com',
            'questionnaire_completed_at' => now(),
            'access_unlocked_at' => now(),
            'resume_analyzed_at' => now(),
            'profile_completed_at' => now(),
        ]);
        $this->mockGoogleUser('candidate@example.com');

        $response = $this->get(route('auth.google.callback'));

        $response->assertRedirect(route('applicant.dashboard'));
    }

    public function test_google_callback_assigns_admin_role_for_configured_admin_email(): void
    {
        config(['services.admin.emails' => ['admin@example.com']]);
        $this->mockGoogleUser('admin@example.com');

        $response = $this->get(route('auth.google.callback'));

        $response->assertRedirect(route('questionnaire.show'));
        $this->assertDatabaseHas('users', [
            'email' => 'admin@example.com',
            'role' => 'admin',
        ]);
    }

    public function test_google_callback_started_from_popup_returns_popup_bridge_page(): void
    {
        $this->mockGoogleUser('Candidate@example.com');

        $response = $this
            ->withSession([
                'google_oauth_popup' => true,
                'google_oauth_opener_origin' => 'http://localhost:8000',
            ])
            ->get(route('auth.google.callback'));

        $response
            ->assertOk()
            ->assertSee('remotematch.google-auth.success')
            ->assertSee('localhost:8000')
            ->assertSee('questionnaire');

        $this->assertAuthenticated('web');
    }

    public function test_google_redirect_canonicalizes_popup_start_to_configured_callback_origin(): void
    {
        config([
            'services.google.client_id' => 'google-client-id',
            'services.google.client_secret' => 'google-client-secret',
            'services.google.redirect' => 'http://127.0.0.1:8000/auth/google/callback',
        ]);

        $response = $this->get('http://localhost:8000/auth/google?popup=1&origin=http://localhost:8000');

        $location = (string) $response->headers->get('location');

        $response->assertRedirect();
        $this->assertStringStartsWith('http://127.0.0.1:8000/auth/google?', $location);
        $this->assertStringContainsString('popup=1', $location);
        $this->assertStringContainsString('origin=http%3A%2F%2Flocalhost%3A8000', $location);
        $this->assertStringContainsString('canonical=1', $location);
    }

    public function test_google_redirect_uses_stateful_oauth_state(): void
    {
        config([
            'services.google.client_id' => 'google-client-id',
            'services.google.client_secret' => 'google-client-secret',
            'services.google.redirect' => 'http://127.0.0.1:8000/auth/google/callback',
        ]);

        $response = $this->get(route('auth.google.redirect', [
            'popup' => 1,
            'origin' => 'http://127.0.0.1:8000',
            'canonical' => 1,
        ]));

        $location = (string) $response->headers->get('location');

        $response->assertRedirect();
        $this->assertStringStartsWith('https://accounts.google.com/o/oauth2/auth?', $location);
        $this->assertStringContainsString('state=', $location);
        $this->assertNotEmpty(session('state'));
    }

    public function test_google_callback_restarts_login_once_when_oauth_state_is_invalid(): void
    {
        $this->mockGoogleInvalidState();

        $response = $this->get(route('auth.google.callback'));

        $response->assertRedirect(route('auth.google.redirect'));
        $response->assertSessionHas('google_oauth_state_retry_attempted', true);
    }

    public function test_google_callback_returns_to_landing_when_oauth_state_retry_already_failed(): void
    {
        $this->mockGoogleInvalidState();

        $response = $this
            ->withSession(['google_oauth_state_retry_attempted' => true])
            ->get(route('auth.google.callback'));

        $response->assertRedirect(route('landing'));
        $response->assertSessionHasErrors('google_login');
    }

    private function mockGoogleUser(string $email): void
    {
        config([
            'services.google.client_id' => 'google-client-id',
            'services.google.client_secret' => 'google-client-secret',
            'services.google.redirect' => 'https://app.example.test/auth/google/callback',
        ]);

        $googleUser = (new SocialiteUser)->map([
            'id' => 'google-123',
            'name' => 'Remote Candidate',
            'email' => $email,
            'avatar' => 'https://example.test/avatar.png',
        ]);

        $provider = Mockery::mock();
        $provider
            ->shouldReceive('user')
            ->once()
            ->andReturn($googleUser);

        Socialite::shouldReceive('driver')
            ->once()
            ->with('google')
            ->andReturn($provider);
    }

    private function mockGoogleInvalidState(): void
    {
        config([
            'services.google.client_id' => 'google-client-id',
            'services.google.client_secret' => 'google-client-secret',
            'services.google.redirect' => 'https://app.example.test/auth/google/callback',
        ]);

        $provider = Mockery::mock();
        $provider
            ->shouldReceive('user')
            ->once()
            ->andThrow(new InvalidStateException);

        Socialite::shouldReceive('driver')
            ->once()
            ->with('google')
            ->andReturn($provider);
    }
}
