<?php

use App\Models\Role;
use App\Models\User;
use App\Services\SystemConfig\SystemConfigService;
use Illuminate\Support\Facades\Cache;
use Laravel\Socialite\Contracts\User as SocialiteUserContract;
use Laravel\Socialite\Facades\Socialite;

beforeEach(function () {
    $ref = new ReflectionClass(SystemConfigService::class);
    $prop = $ref->getProperty('resolvedSettings');
    $prop->setAccessible(true);
    $prop->setValue(null, null);
    Cache::flush();
    Role::firstOrCreate(['name' => 'User', 'guard_name' => 'web']);
});

function enableOauth(string $provider): void
{
    app(SystemConfigService::class)->update(['feature_'.$provider.'_oauth' => true]);
}

function fakeSocialiteUser(array $overrides = []): SocialiteUserContract
{
    $u = new Laravel\Socialite\Two\User;
    $u->id = $overrides['id'] ?? 'oauth-id-123';
    $u->name = $overrides['name'] ?? 'OAuth User';
    $u->email = $overrides['email'] ?? 'oauth@example.com';
    $u->avatar = $overrides['avatar'] ?? 'https://example.com/avatar.png';
    $u->user = $overrides['user'] ?? [];

    return $u;
}

test('redirect returns 404 when provider feature is disabled', function () {
    $this->get('/auth/google')->assertNotFound();
});

test('redirect issues a redirect when provider feature is enabled', function () {
    enableOauth('google');

    Socialite::shouldReceive('driver->redirect')
        ->andReturn(redirect('https://accounts.google.com/o/oauth2/auth?fake=1'));

    $this->get('/auth/google')->assertRedirect();
    expect(session('social_auth_provider'))->toBe('google');
});

test('callback without provider session redirects to login with error', function () {
    $this->get('/auth/callback')
        ->assertRedirect('/login')
        ->assertSessionHas('error');
});

test('callback rejects unverified email from provider', function () {
    enableOauth('google');
    session(['social_auth_provider' => 'google']);

    Socialite::shouldReceive('driver->user')->andReturn(fakeSocialiteUser([
        'user' => ['email_verified' => false],
    ]));

    $this->get('/auth/callback')
        ->assertRedirect('/login')
        ->assertSessionHas('error');

    $this->assertGuest();
});

test('callback creates a new user via provider id and assigns user role', function () {
    enableOauth('google');
    session(['social_auth_provider' => 'google']);

    Socialite::shouldReceive('driver->user')->andReturn(fakeSocialiteUser([
        'id' => 'google-uid-9',
        'email' => 'fresh@example.com',
        'name' => 'Fresh User',
    ]));

    $this->get('/auth/callback')->assertRedirect('/dashboard');

    $user = User::where('email', 'fresh@example.com')->first();
    expect($user)->not->toBeNull();
    expect($user->google_id)->toBe('google-uid-9');
    expect($user->hasRole('User'))->toBeTrue();
});

test('callback links to existing user with matching email when no provider id yet', function () {
    enableOauth('google');
    session(['social_auth_provider' => 'google']);
    $existing = User::factory()->create([
        'email' => 'link@example.com',
        'google_id' => null,
    ]);

    Socialite::shouldReceive('driver->user')->andReturn(fakeSocialiteUser([
        'id' => 'google-uid-link',
        'email' => 'link@example.com',
    ]));

    $this->get('/auth/callback')->assertRedirect('/dashboard');

    expect($existing->fresh()->google_id)->toBe('google-uid-link');
    $this->assertAuthenticatedAs($existing->fresh());
});

test('callback refuses to overwrite a different existing oauth identity', function () {
    enableOauth('google');
    session(['social_auth_provider' => 'google']);

    $existing = User::factory()->create([
        'email' => 'taken@example.com',
        'google_id' => 'different-google-id',
    ]);

    Socialite::shouldReceive('driver->user')->andReturn(fakeSocialiteUser([
        'id' => 'attacker-id',
        'email' => 'taken@example.com',
    ]));

    $this->get('/auth/callback')
        ->assertRedirect('/login')
        ->assertSessionHas('error');

    expect($existing->fresh()->google_id)->toBe('different-google-id');
    $this->assertGuest();
});

test('callback re-uses user matched by provider id', function () {
    enableOauth('google');
    session(['social_auth_provider' => 'google']);

    $existing = User::factory()->create(['google_id' => 'stable-id']);

    Socialite::shouldReceive('driver->user')->andReturn(fakeSocialiteUser([
        'id' => 'stable-id',
        'email' => $existing->email,
    ]));

    $this->get('/auth/callback')->assertRedirect('/dashboard');
    $this->assertAuthenticatedAs($existing->fresh());
});

test('callback on socialite exception redirects to login with error', function () {
    enableOauth('google');
    session(['social_auth_provider' => 'google']);

    Socialite::shouldReceive('driver->user')->andThrow(new Exception('OAuth boom'));

    $this->get('/auth/callback')
        ->assertRedirect('/login')
        ->assertSessionHas('error');

    $this->assertGuest();
});
