<?php

declare(strict_types=1);

use Builder\Core\Foundation\Concerns\HasBuilderAccess;
use Builder\Core\Foundation\Contracts\BuilderAccessible;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Facades\Schema;

final class InviteTestUser extends Authenticatable implements BuilderAccessible
{
    use HasBuilderAccess;
    use Notifiable;

    protected $table = 'users';

    protected $guarded = [];

    public function canAccessBuilder(): bool
    {
        return true;
    }

    /** @return list<string> */
    public function builderPermissions(): array
    {
        return ['*'];
    }
}

beforeEach(function (): void {
    Schema::create('users', function ($table): void {
        $table->id();
        $table->string('name')->nullable();
        $table->string('email')->nullable()->unique();
        $table->string('password')->nullable();
        $table->timestamp('activated_at')->nullable();
        $table->rememberToken();
        $table->timestamps();
    });

    Schema::create('password_reset_tokens', function ($table): void {
        $table->string('email')->primary();
        $table->string('token');
        $table->timestamp('created_at')->nullable();
    });

    config([
        'auth.providers.users.model' => InviteTestUser::class,
        'auth.passwords.builder-invite' => [
            'provider' => 'users',
            'table' => 'password_reset_tokens',
            'expire' => 1440,
            'throttle' => 60,
        ],
    ]);
});

it('shows accept invite page with valid token', function (): void {
    $user = InviteTestUser::create([
        'name' => 'Jane',
        'email' => 'jane@example.com',
        'password' => Hash::make('old'),
    ]);

    $token = Password::broker('builder-invite')->createToken($user);

    $this->get(route('builder.invite.show', $token).'?email=jane@example.com')
        ->assertInertia(fn ($page) => $page->component('Auth/AcceptInvite')
            ->where('token', $token)
            ->where('email', 'jane@example.com')
        );
});

it('redirects with error for invalid token', function (): void {
    $this->get(route('builder.invite.show', 'invalid-token').'?email=nobody@example.com')
        ->assertRedirect(route('builder.login'));
});

it('sets password and logs in user with valid token', function (): void {
    $user = InviteTestUser::create([
        'name' => 'Jane',
        'email' => 'jane@example.com',
        'password' => Hash::make('old'),
    ]);

    $token = Password::broker('builder-invite')->createToken($user);

    $this->post(route('builder.invite.store', $token), [
        'email' => 'jane@example.com',
        'password' => 'newpassword123',
        'password_confirmation' => 'newpassword123',
    ])
        ->assertRedirect(route('builder.dashboard'));

    $user->refresh();
    expect(Hash::check('newpassword123', $user->password))->toBeTrue()
        ->and($user->activated_at)->not->toBeNull();

    $guard = config('builder.auth.guard', 'web');
    expect(Auth::guard($guard)->check())->toBeTrue();
});

it('rejects mismatched password confirmation', function (): void {
    $user = InviteTestUser::create([
        'name' => 'Jane',
        'email' => 'jane@example.com',
        'password' => Hash::make('old'),
    ]);

    $token = Password::broker('builder-invite')->createToken($user);

    $this->post(route('builder.invite.store', $token), [
        'email' => 'jane@example.com',
        'password' => 'newpassword123',
        'password_confirmation' => 'different',
    ])
        ->assertSessionHasErrors('password');
});

it('rejects invalid token on store', function (): void {
    InviteTestUser::create([
        'name' => 'Jane',
        'email' => 'jane@example.com',
        'password' => Hash::make('old'),
    ]);

    $this->post(route('builder.invite.store', 'bad-token'), [
        'email' => 'jane@example.com',
        'password' => 'newpassword123',
        'password_confirmation' => 'newpassword123',
    ])
        ->assertSessionHasErrors('email');
});
