<?php

use App\Enums\Permission as PermissionEnum;
use App\Models\Employee;
use App\Models\User;
use App\Models\UserProfile;
use Database\Seeders\RolePermissionSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

beforeEach(function () {
    $this->seed(RolePermissionSeeder::class);
});

// ─── Helper ───────────────────────────────────────────────

function createEmployeeUserWithPermission(PermissionEnum ...$permissions): User
{
    $user = User::factory()->create();

    $user->givePermissionTo(
        array_merge(
            [PermissionEnum::DASHBOARD_VIEW->value],
            array_map(fn (PermissionEnum $p) => $p->value, $permissions)
        )
    );

    $user->forgetCachedPermissions();

    return $user;
}

function createEmployeeWithProfile(): User
{
    $user = User::factory()->create();
    UserProfile::factory()->create([
        'user_id' => $user->id,
        'full_name' => fake()->name(),
        'phone_number' => fake()->numerify('08##########'),
        'gender' => fake()->randomElement(['male', 'female']),
    ]);
    Employee::factory()->create(['user_id' => $user->id]);
    $user->assignRole('marketing');

    return $user;
}

function createDeveloperUser(): User
{
    $user = User::factory()->create();
    UserProfile::factory()->create([
        'user_id' => $user->id,
        'full_name' => fake()->name(),
        'phone_number' => fake()->numerify('08##########'),
        'gender' => fake()->randomElement(['male', 'female']),
    ]);
    $user->assignRole('developer');

    return $user;
}

function validEmployeePayload(): array
{
    return [
        'email' => 'pegawai@example.com',
        'username' => 'pegawai_baru',
        'full_name' => 'Pegawai Baru',
        'phone_number' => '081234567890',
        'gender' => 'male',
        'birth_date' => '1995-01-15',
        'address' => 'Jl. Merdeka No. 1',
        'role' => 'marketing',
        'join_date' => '2024-01-01',
        'employment_status' => 'full_time',
        'base_salary' => 5000000,
    ];
}

// ─── Index ────────────────────────────────────────────────

describe('Employee Index', function () {
    test('authenticated user with permission can view employee index', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);

        $this->actingAs($user)
            ->get(route('admin.hr.employees.index'))
            ->assertOk();
    });

    test('guest is redirected to login', function () {
        $this->get(route('admin.hr.employees.index'))
            ->assertRedirect(route('login'));
    });

    test('user without permission is forbidden', function () {
        $user = User::factory()->create();

        $this->actingAs($user)
            ->get(route('admin.hr.employees.index'))
            ->assertForbidden();
    });

    test('index displays employees', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);

        createEmployeeWithProfile();
        createEmployeeWithProfile();

        $this->actingAs($user)
            ->get(route('admin.hr.employees.index'))
            ->assertOk();
    });

    test('index can search by name', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);

        $employee = createEmployeeWithProfile();
        $employee->profile->update(['full_name' => 'Budi Santoso']);

        $this->actingAs($user)
            ->get(route('admin.hr.employees.index', ['search' => 'Budi']))
            ->assertOk();
    });

    test('index can filter by role', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);

        createEmployeeWithProfile();

        $this->actingAs($user)
            ->get(route('admin.hr.employees.index', ['role' => 'marketing']))
            ->assertOk();
    });

    test('index can filter by is_active status', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);

        createEmployeeWithProfile();

        $this->actingAs($user)
            ->get(route('admin.hr.employees.index', ['is_active' => '1']))
            ->assertOk();
    });

    test('index does not display developer role users', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);

        $developer = createDeveloperUser();
        $employee = createEmployeeWithProfile();

        $this->actingAs($user)
            ->get(route('admin.hr.employees.index'))
            ->assertOk()
            ->assertDontSee($developer->email)
            ->assertSee($employee->email);
    });
});

// ─── Create ───────────────────────────────────────────────

describe('Employee Create', function () {
    test('authenticated user with permission can view create form', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);

        $this->actingAs($user)
            ->get(route('admin.hr.employees.create'))
            ->assertOk();
    });

    test('guest is redirected to login', function () {
        $this->get(route('admin.hr.employees.create'))
            ->assertRedirect(route('login'));
    });

    test('user without create permission is forbidden', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);

        $this->actingAs($user)
            ->get(route('admin.hr.employees.create'))
            ->assertForbidden();
    });
});

// ─── Store ────────────────────────────────────────────────

describe('Employee Store', function () {
    test('authenticated user with permission can create an employee', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);

        $this->actingAs($user)
            ->post(route('admin.hr.employees.store'), validEmployeePayload())
            ->assertRedirect(route('admin.hr.employees.index'));

        $this->assertDatabaseHas('users', [
            'email' => 'pegawai@example.com',
            'username' => 'pegawai_baru',
        ]);
    });

    test('guest cannot create an employee', function () {
        $this->post(route('admin.hr.employees.store'), validEmployeePayload())
            ->assertRedirect(route('login'));
    });

    test('user without create permission is forbidden', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);

        $this->actingAs($user)
            ->post(route('admin.hr.employees.store'), validEmployeePayload())
            ->assertForbidden();
    });

    test('email is required', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);

        $payload = validEmployeePayload();
        $payload['email'] = '';

        $this->actingAs($user)
            ->post(route('admin.hr.employees.store'), $payload)
            ->assertSessionHasErrors('email');
    });

    test('email must be unique', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);

        $existing = User::factory()->create();
        $payload = validEmployeePayload();
        $payload['email'] = $existing->email;

        $this->actingAs($user)
            ->post(route('admin.hr.employees.store'), $payload)
            ->assertSessionHasErrors('email');
    });

    test('username is required', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);

        $payload = validEmployeePayload();
        $payload['username'] = '';

        $this->actingAs($user)
            ->post(route('admin.hr.employees.store'), $payload)
            ->assertSessionHasErrors('username');
    });

    test('username must be unique', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);

        $existing = User::factory()->create();
        $payload = validEmployeePayload();
        $payload['username'] = $existing->username;

        $this->actingAs($user)
            ->post(route('admin.hr.employees.store'), $payload)
            ->assertSessionHasErrors('username');
    });

    test('full_name is required', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);

        $payload = validEmployeePayload();
        $payload['full_name'] = '';

        $this->actingAs($user)
            ->post(route('admin.hr.employees.store'), $payload)
            ->assertSessionHasErrors('full_name');
    });

    test('role is required', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);

        $payload = validEmployeePayload();
        $payload['role'] = '';

        $this->actingAs($user)
            ->post(route('admin.hr.employees.store'), $payload)
            ->assertSessionHasErrors('role');
    });

    test('role must be assignable', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);

        $payload = validEmployeePayload();
        $payload['role'] = 'developer';

        $this->actingAs($user)
            ->post(route('admin.hr.employees.store'), $payload)
            ->assertSessionHasErrors('role');
    });

    test('join_date is required for non-owner roles', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);

        $payload = validEmployeePayload();
        $payload['join_date'] = '';

        $this->actingAs($user)
            ->post(route('admin.hr.employees.store'), $payload)
            ->assertSessionHasErrors('join_date');
    });

    test('employment_status is required for non-owner roles', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);

        $payload = validEmployeePayload();
        $payload['employment_status'] = '';

        $this->actingAs($user)
            ->post(route('admin.hr.employees.store'), $payload)
            ->assertSessionHasErrors('employment_status');
    });

    test('base_salary is required for non-owner roles', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);

        $payload = validEmployeePayload();
        $payload['base_salary'] = '';

        $this->actingAs($user)
            ->post(route('admin.hr.employees.store'), $payload)
            ->assertSessionHasErrors('base_salary');
    });

    test('phone_number must be valid format', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);

        $payload = validEmployeePayload();
        $payload['phone_number'] = '123invalid';

        $this->actingAs($user)
            ->post(route('admin.hr.employees.store'), $payload)
            ->assertSessionHasErrors('phone_number');
    });

    test('creating employee also creates profile and employee record', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_CREATE);

        $this->actingAs($user)
            ->post(route('admin.hr.employees.store'), validEmployeePayload());

        $newUser = User::where('email', 'pegawai@example.com')->first();
        expect($newUser)->not->toBeNull();
        expect($newUser->profile)->not->toBeNull();
        expect($newUser->employee)->not->toBeNull();
        expect($newUser->profile->full_name)->toBe('Pegawai Baru');
        expect($newUser->employee->base_salary)->toBe(5000000);
    });
});

// ─── Edit ─────────────────────────────────────────────────

describe('Employee Edit', function () {
    test('authenticated user with permission can view edit form', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_UPDATE);

        $employee = createEmployeeWithProfile();

        $this->actingAs($user)
            ->get(route('admin.hr.employees.edit', $employee))
            ->assertOk();
    });

    test('guest is redirected to login', function () {
        $employee = createEmployeeWithProfile();

        $this->get(route('admin.hr.employees.edit', $employee))
            ->assertRedirect(route('login'));
    });

    test('user without update permission is forbidden', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);

        $employee = createEmployeeWithProfile();

        $this->actingAs($user)
            ->get(route('admin.hr.employees.edit', $employee))
            ->assertForbidden();
    });
});

// ─── Update ───────────────────────────────────────────────

describe('Employee Update', function () {
    test('authenticated user with permission can update an employee', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_UPDATE);

        $employee = createEmployeeWithProfile();

        $payload = validEmployeePayload();
        $payload['email'] = $employee->email;
        $payload['username'] = $employee->username;
        $payload['full_name'] = 'Nama Diubah';

        $this->actingAs($user)
            ->put(route('admin.hr.employees.update', $employee), $payload)
            ->assertRedirect(route('admin.hr.employees.index'));

        expect($employee->fresh()->profile->full_name)->toBe('Nama Diubah');
    });

    test('guest cannot update an employee', function () {
        $employee = createEmployeeWithProfile();

        $this->put(route('admin.hr.employees.update', $employee), validEmployeePayload())
            ->assertRedirect(route('login'));
    });

    test('user without update permission is forbidden', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);

        $employee = createEmployeeWithProfile();

        $this->actingAs($user)
            ->put(route('admin.hr.employees.update', $employee), validEmployeePayload())
            ->assertForbidden();
    });

    test('email must be unique on update (ignoring self)', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_UPDATE);

        $employee = createEmployeeWithProfile();

        $payload = validEmployeePayload();
        $payload['email'] = $employee->email;
        $payload['username'] = $employee->username;

        $this->actingAs($user)
            ->put(route('admin.hr.employees.update', $employee), $payload)
            ->assertRedirect(route('admin.hr.employees.index'));
    });
});

// ─── Toggle Status ────────────────────────────────────────

describe('Employee Toggle Status', function () {
    test('authenticated user with permission can toggle status', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_TOGGLE_STATUS);

        $employee = createEmployeeWithProfile();
        $originalStatus = $employee->is_active;

        $this->actingAs($user)
            ->patch(route('admin.hr.employees.toggle_status', $employee), [
                'is_active' => ! $originalStatus,
            ])
            ->assertRedirect(route('admin.hr.employees.index'));

        expect($employee->fresh()->is_active)->toBe(! $originalStatus);
    });

    test('guest cannot toggle status', function () {
        $employee = createEmployeeWithProfile();

        $this->patch(route('admin.hr.employees.toggle_status', $employee), [
            'is_active' => false,
        ])->assertRedirect(route('login'));
    });

    test('user without toggle permission is forbidden', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);

        $employee = createEmployeeWithProfile();

        $this->actingAs($user)
            ->patch(route('admin.hr.employees.toggle_status', $employee), [
                'is_active' => false,
            ])
            ->assertForbidden();
    });

    test('is_active field is required', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_TOGGLE_STATUS);

        $employee = createEmployeeWithProfile();

        $this->actingAs($user)
            ->patch(route('admin.hr.employees.toggle_status', $employee), [
                'is_active' => null,
            ])
            ->assertSessionHasErrors('is_active');
    });
});

// ─── Reset Password ───────────────────────────────────────

describe('Employee Reset Password', function () {
    test('authenticated user with permission can reset password', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_RESET_PASSWORD);

        $employee = createEmployeeWithProfile();

        $this->actingAs($user)
            ->post(route('admin.hr.employees.reset_password', $employee))
            ->assertRedirect(route('admin.hr.employees.index'));
    });

    test('guest cannot reset password', function () {
        $employee = createEmployeeWithProfile();

        $this->post(route('admin.hr.employees.reset_password', $employee))
            ->assertRedirect(route('login'));
    });

    test('user without reset password permission is forbidden', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);

        $employee = createEmployeeWithProfile();

        $this->actingAs($user)
            ->post(route('admin.hr.employees.reset_password', $employee))
            ->assertForbidden();
    });
});

// ─── Destroy ──────────────────────────────────────────────

describe('Employee Destroy', function () {
    test('authenticated user with permission can delete an employee', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_DELETE);

        $employee = createEmployeeWithProfile();

        $this->actingAs($user)
            ->delete(route('admin.hr.employees.destroy', $employee))
            ->assertRedirect(route('admin.hr.employees.index'));

        $this->assertSoftDeleted('users', ['id' => $employee->id]);
    });

    test('guest cannot delete an employee', function () {
        $employee = createEmployeeWithProfile();

        $this->delete(route('admin.hr.employees.destroy', $employee))
            ->assertRedirect(route('login'));

        $this->assertNotSoftDeleted('users', ['id' => $employee->id]);
    });

    test('user without delete permission is forbidden', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW);

        $employee = createEmployeeWithProfile();

        $this->actingAs($user)
            ->delete(route('admin.hr.employees.destroy', $employee))
            ->assertForbidden();

        $this->assertNotSoftDeleted('users', ['id' => $employee->id]);
    });

    test('deleting employee also soft deletes profile and employee record', function () {
        $user = createEmployeeUserWithPermission(PermissionEnum::EMPLOYEES_VIEW, PermissionEnum::EMPLOYEES_DELETE);

        $employee = createEmployeeWithProfile();
        $profileId = $employee->profile->id;
        $employeeId = $employee->employee->id;

        $this->actingAs($user)
            ->delete(route('admin.hr.employees.destroy', $employee));

        $this->assertSoftDeleted('users', ['id' => $employee->id]);
        $this->assertSoftDeleted('user_profiles', ['id' => $profileId]);
        $this->assertSoftDeleted('employees', ['id' => $employeeId]);
    });
});
