<?php

use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Hash;
use Inertia\Testing\AssertableInertia as Assert;

uses(RefreshDatabase::class);

/*
|--------------------------------------------------------------------------
| AUTHENTICATION
|--------------------------------------------------------------------------
*/

test('guests are redirected to the login page for profile edit', function () {
    $response = $this->get(route('profile.edit'));
    $response->assertRedirect(route('login'));
});

test('guests are redirected to the login page for profile update', function () {
    $response = $this->patch(route('profile.update'), [
        'email' => 'test@example.com',
        'username' => 'testuser',
        'full_name' => 'Test User',
    ]);
    $response->assertRedirect(route('login'));
});

/*
|--------------------------------------------------------------------------
| PROFILE EDIT PAGE
|--------------------------------------------------------------------------
*/

test('profile page is displayed', function () {
    $user = User::factory()->create();

    $response = $this
        ->actingAs($user)
        ->get(route('profile.edit'));

    $response->assertOk();
});

test('profile page renders correct inertia component', function () {
    $user = User::factory()->create();

    $this->actingAs($user)
        ->get(route('profile.edit'))
        ->assertInertia(fn (Assert $page) => $page
            ->component('settings/profile')
        );
});

test('profile page passes user data to view', function () {
    $user = User::factory()->create([
        'email' => 'john@example.com',
        'username' => 'johndoe',
    ]);
    $user->userProfile()->create([
        'full_name' => 'John Doe',
        'phone_number' => '08123456789',
        'gender' => 'male',
        'birth_date' => '1990-05-15',
        'address' => 'Jl. Sudirman No. 1',
    ]);

    $this->actingAs($user)
        ->get(route('profile.edit'))
        ->assertInertia(fn (Assert $page) => $page
            ->where('user.id', $user->id)
            ->where('user.email', 'john@example.com')
            ->where('user.username', 'johndoe')
            ->where('user.userProfile.full_name', 'John Doe')
            ->where('user.userProfile.phone_number', '08123456789')
            ->where('user.userProfile.gender', 'male')
            ->where('user.userProfile.birth_date', '1990-05-15')
            ->where('user.userProfile.address', 'Jl. Sudirman No. 1')
        );
});

test('profile page works when user has no profile', function () {
    $user = User::factory()->create();

    $this->actingAs($user)
        ->get(route('profile.edit'))
        ->assertInertia(fn (Assert $page) => $page
            ->where('user.id', $user->id)
            ->where('user.email', $user->email)
            ->where('user.username', $user->username)
            ->where('user.userProfile', null)
        );
});

test('profile page passes mustVerifyEmail flag', function () {
    $user = User::factory()->create();

    $this->actingAs($user)
        ->get(route('profile.edit'))
        ->assertInertia(fn (Assert $page) => $page
            ->has('mustVerifyEmail')
        );
});

/*
|--------------------------------------------------------------------------
| PROFILE UPDATE - SUCCESS
|--------------------------------------------------------------------------
*/

test('profile information can be updated with all fields', function () {
    $user = User::factory()->create(['username' => 'originaluser']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'updateduser',
            'full_name' => 'Updated Name',
            'phone_number' => '08987654321',
            'gender' => 'female',
            'birth_date' => '1995-03-20',
            'address' => 'Jl. Thamrin No. 2',
        ]);

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

    $user->refresh();

    expect($user->username)->toBe('updateduser');
    expect($user->userProfile->full_name)->toBe('Updated Name');
    expect($user->userProfile->phone_number)->toBe('08987654321');
    expect($user->userProfile->gender->value)->toBe('female');
    expect($user->userProfile->birth_date->format('Y-m-d'))->toBe('1995-03-20');
    expect($user->userProfile->address)->toBe('Jl. Thamrin No. 2');
});

test('profile can be updated with only required fields', function () {
    $user = User::factory()->create(['username' => 'minimaluser']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'minimaluser',
            'full_name' => 'Minimal Update',
        ]);

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

    expect($user->refresh()->userProfile->full_name)->toBe('Minimal Update');
});

test('profile update creates user profile if not exists', function () {
    $user = User::factory()->create(['username' => 'newprofile']);
    expect($user->userProfile)->toBeNull();

    $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'newprofile',
            'full_name' => 'New Profile',
            'phone_number' => '08111111111',
            'gender' => 'male',
            'birth_date' => '2000-01-01',
            'address' => 'New Address',
        ]);

    $user->refresh();
    expect($user->userProfile)->not->toBeNull();
    expect($user->userProfile->full_name)->toBe('New Profile');
    expect($user->userProfile->phone_number)->toBe('08111111111');
});

test('profile update updates existing user profile', function () {
    $user = User::factory()->create(['username' => 'existingprofile']);
    $user->userProfile()->create([
        'full_name' => 'Old Name',
        'phone_number' => '08000000000',
        'gender' => 'male',
        'address' => 'Old Address',
    ]);

    $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'existingprofile',
            'full_name' => 'New Name',
            'phone_number' => '08999999999',
            'gender' => 'female',
            'address' => 'New Address',
        ]);

    $user->refresh();
    expect($user->userProfile->full_name)->toBe('New Name');
    expect($user->userProfile->phone_number)->toBe('08999999999');
    expect($user->userProfile->gender->value)->toBe('female');
    expect($user->userProfile->address)->toBe('New Address');
    $this->assertDatabaseCount('user_profiles', 1);
});

test('profile update with null optional fields saves nulls', function () {
    $user = User::factory()->create(['username' => 'nulltest']);
    $user->userProfile()->create([
        'full_name' => 'Has Data',
        'phone_number' => '08123456789',
        'gender' => 'male',
        'birth_date' => '1990-01-01',
        'address' => 'Some Address',
    ]);

    $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'nulltest',
            'full_name' => 'Still Has Name',
            'phone_number' => null,
            'gender' => null,
            'birth_date' => null,
            'address' => null,
        ]);

    $user->refresh();
    expect($user->userProfile->full_name)->toBe('Still Has Name');
    expect($user->userProfile->phone_number)->toBeNull();
    expect($user->userProfile->gender)->toBeNull();
    expect($user->userProfile->birth_date)->toBeNull();
    expect($user->userProfile->address)->toBeNull();
});

/*
|--------------------------------------------------------------------------
| PROFILE UPDATE - VALIDATION: EMAIL
|--------------------------------------------------------------------------
*/

test('email is required', function () {
    $user = User::factory()->create(['username' => 'emailreq']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'username' => 'emailreq',
            'full_name' => 'Test',
        ]);

    $response->assertSessionHasErrors('email');
});

test('email must be valid format', function () {
    $user = User::factory()->create(['username' => 'emailfmt']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => 'not-an-email',
            'username' => 'emailfmt',
            'full_name' => 'Test',
        ]);

    $response->assertSessionHasErrors('email');
});

test('email must not exceed 255 characters', function () {
    $user = User::factory()->create(['username' => 'emailmax']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => str_repeat('a', 246).'@example.com',
            'username' => 'emailmax',
            'full_name' => 'Test',
        ]);

    $response->assertSessionHasErrors('email');
});

test('email must be unique excluding self', function () {
    $user = User::factory()->create(['email' => 'user1@example.com', 'username' => 'uniqueemail1']);
    User::factory()->create(['email' => 'user2@example.com', 'username' => 'uniqueemail2']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => 'user2@example.com',
            'username' => 'uniqueemail1',
            'full_name' => 'Test',
        ]);

    $response->assertSessionHasErrors('email');
});

test('user can keep their own email on update', function () {
    $user = User::factory()->create(['email' => 'keep@example.com', 'username' => 'keepemail']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => 'keep@example.com',
            'username' => 'keepemail',
            'full_name' => 'Test',
        ]);

    $response->assertSessionHasNoErrors();
});

/*
|--------------------------------------------------------------------------
| PROFILE UPDATE - VALIDATION: USERNAME
|--------------------------------------------------------------------------
*/

test('username is required', function () {
    $user = User::factory()->create(['username' => 'usernamereq']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'full_name' => 'Test',
        ]);

    $response->assertSessionHasErrors('username');
});

test('username must not exceed 20 characters', function () {
    $user = User::factory()->create(['username' => 'usernamemax']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => str_repeat('a', 21),
            'full_name' => 'Test',
        ]);

    $response->assertSessionHasErrors('username');
});

test('username must be alpha_dash', function () {
    $user = User::factory()->create(['username' => 'alphauser']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'has spaces!',
            'full_name' => 'Test',
        ]);

    $response->assertSessionHasErrors('username');
});

test('username allows dashes and underscores', function () {
    $user = User::factory()->create(['username' => 'dashuser']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'user-name_123',
            'full_name' => 'Test',
        ]);

    $response->assertSessionHasNoErrors();
});

test('username must be unique excluding self', function () {
    $user = User::factory()->create(['username' => 'uniqueuser1']);
    User::factory()->create(['username' => 'uniqueuser2']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'uniqueuser2',
            'full_name' => 'Test',
        ]);

    $response->assertSessionHasErrors('username');
});

test('user can keep their own username on update', function () {
    $user = User::factory()->create(['username' => 'keepuser']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'keepuser',
            'full_name' => 'Test',
        ]);

    $response->assertSessionHasNoErrors();
});

/*
|--------------------------------------------------------------------------
| PROFILE UPDATE - VALIDATION: FULL NAME
|--------------------------------------------------------------------------
*/

test('full_name is required', function () {
    $user = User::factory()->create(['username' => 'fullnamereq']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'fullnamereq',
        ]);

    $response->assertSessionHasErrors('full_name');
});

test('full_name must not exceed 200 characters', function () {
    $user = User::factory()->create(['username' => 'fullnamemax']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'fullnamemax',
            'full_name' => str_repeat('a', 201),
        ]);

    $response->assertSessionHasErrors('full_name');
});

/*
|--------------------------------------------------------------------------
| PROFILE UPDATE - VALIDATION: OPTIONAL FIELDS
|--------------------------------------------------------------------------
*/

test('phone_number is optional', function () {
    $user = User::factory()->create(['username' => 'phonetest']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'phonetest',
            'full_name' => 'Test',
            'phone_number' => null,
        ]);

    $response->assertSessionHasNoErrors();
});

test('phone_number must not exceed 20 characters', function () {
    $user = User::factory()->create(['username' => 'phonemax']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'phonemax',
            'full_name' => 'Test',
            'phone_number' => str_repeat('1', 21),
        ]);

    $response->assertSessionHasErrors('phone_number');
});

test('gender must be valid enum value', function () {
    $user = User::factory()->create(['username' => 'genderbad']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'genderbad',
            'full_name' => 'Test',
            'gender' => 'other',
        ]);

    $response->assertSessionHasErrors('gender');
});

test('gender accepts male', function () {
    $user = User::factory()->create(['username' => 'gendermale']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'gendermale',
            'full_name' => 'Test',
            'gender' => 'male',
        ]);

    $response->assertSessionHasNoErrors();
});

test('gender accepts female', function () {
    $user = User::factory()->create(['username' => 'genderfemale']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'genderfemale',
            'full_name' => 'Test',
            'gender' => 'female',
        ]);

    $response->assertSessionHasNoErrors();
});

test('birth_date is optional', function () {
    $user = User::factory()->create(['username' => 'birthnull']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'birthnull',
            'full_name' => 'Test',
            'birth_date' => null,
        ]);

    $response->assertSessionHasNoErrors();
});

test('birth_date must be valid date', function () {
    $user = User::factory()->create(['username' => 'birthbad']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'birthbad',
            'full_name' => 'Test',
            'birth_date' => 'not-a-date',
        ]);

    $response->assertSessionHasErrors('birth_date');
});

test('address is optional', function () {
    $user = User::factory()->create(['username' => 'addrnull']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'addrnull',
            'full_name' => 'Test',
            'address' => null,
        ]);

    $response->assertSessionHasNoErrors();
});

/*
|--------------------------------------------------------------------------
| PROFILE UPDATE - VALIDATION: EMPTY REQUEST
|--------------------------------------------------------------------------
*/

test('user cannot update profile without submitting any data', function () {
    $user = User::factory()->create(['username' => 'emptyreq']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), []);

    $response->assertSessionHasErrors(['email', 'username', 'full_name']);
});

/*
|--------------------------------------------------------------------------
| PROFILE UPDATE - TOAST / FLASH
|--------------------------------------------------------------------------
*/

test('profile update flashes success toast via inertia', function () {
    $user = User::factory()->create(['username' => 'toastuser']);

    $response = $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'toastuser',
            'full_name' => 'Toast Test',
        ]);

    $response->assertRedirect();
});

/*
|--------------------------------------------------------------------------
| PROFILE UPDATE - DATA INTEGRITY
|--------------------------------------------------------------------------
*/

test('profile update preserves other user fields', function () {
    $user = User::factory()->create(['username' => 'preservetest']);

    $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'preservetest',
            'full_name' => 'New Name',
        ]);

    $user->refresh();
    expect(Hash::check('password', $user->password))->toBeTrue();
});

test('profile update with realistic indonesian data', function () {
    $user = User::factory()->create(['email' => 'budi.santoso@gmail.com', 'username' => 'budisan']);

    $this
        ->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => 'budi.santoso@gmail.com',
            'username' => 'budisan',
            'full_name' => 'Budi Santoso',
            'phone_number' => '08123456789',
            'gender' => 'male',
            'birth_date' => '1990-05-15',
            'address' => 'Jl. Sudirman No. 123, Jakarta Selatan',
        ]);

    $user->refresh();
    expect($user->email)->toBe('budi.santoso@gmail.com');
    expect($user->username)->toBe('budisan');
    expect($user->userProfile->full_name)->toBe('Budi Santoso');
    expect($user->userProfile->phone_number)->toBe('08123456789');
    expect($user->userProfile->gender->value)->toBe('male');
    expect($user->userProfile->birth_date->format('Y-m-d'))->toBe('1990-05-15');
    expect($user->userProfile->address)->toBe('Jl. Sudirman No. 123, Jakarta Selatan');
});

/*
|--------------------------------------------------------------------------
| ROUTE TESTS
|--------------------------------------------------------------------------
*/

test('settings redirect goes to profile edit', function () {
    $user = User::factory()->create(['username' => 'redirtest']);

    $response = $this
        ->actingAs($user)
        ->get('/settings');

    $response->assertRedirect('/settings/profile');
});

/*
|--------------------------------------------------------------------------
| REALISTIC SCENARIOS
|--------------------------------------------------------------------------
*/

test('user updates profile then views it again', function () {
    $user = User::factory()->create(['username' => 'scenariouser']);

    $this->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'scenariouser',
            'full_name' => 'Scenario User',
            'phone_number' => '08123456789',
            'gender' => 'female',
            'birth_date' => '1995-08-20',
            'address' => 'Jl. Gatot Subroto No. 45',
        ]);

    $this->actingAs($user->fresh())
        ->get(route('profile.edit'))
        ->assertInertia(fn (Assert $page) => $page
            ->where('user.userProfile.full_name', 'Scenario User')
            ->where('user.userProfile.phone_number', '08123456789')
            ->where('user.userProfile.gender', 'female')
            ->where('user.userProfile.birth_date', '1995-08-20')
            ->where('user.userProfile.address', 'Jl. Gatot Subroto No. 45')
        );
});

test('user updates profile multiple times keeps only latest data', function () {
    $user = User::factory()->create(['username' => 'multitimes']);

    $this->actingAs($user)
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'multitimes',
            'full_name' => 'First Update',
            'phone_number' => '08111111111',
        ]);

    $this->actingAs($user->fresh())
        ->patch(route('profile.update'), [
            'email' => $user->email,
            'username' => 'multitimes',
            'full_name' => 'Second Update',
            'phone_number' => '08222222222',
        ]);

    $user->refresh();
    expect($user->userProfile->full_name)->toBe('Second Update');
    expect($user->userProfile->phone_number)->toBe('08222222222');
    $this->assertDatabaseCount('user_profiles', 1);
});
