<?php

use App\Models\Attendance;
use App\Models\Employee;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Inertia\Testing\AssertableInertia as Assert;

uses(RefreshDatabase::class);

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

test('guests are redirected to the login page', function () {
    $response = $this->get(route('admin.hr.attendances.index'));
    $response->assertRedirect(route('login'));
});

test('authenticated users can visit the attendance index page', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $response = $this->get(route('admin.hr.attendances.index'));
    $response->assertOk();
});

/*
|--------------------------------------------------------------------------
| INDEX PAGE
|--------------------------------------------------------------------------
*/

test('index page displays attendances', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $employeeUser = User::factory()->create();
    $employeeUser->employee()->create([
        'join_date' => '2024-01-01',
        'employment_status' => 'full_time',
        'base_salary' => 5000000,
    ]);

    Attendance::factory()->create([
        'employee_id' => $employeeUser->employee->id,
        'attendance_date' => now()->toDateString(),
        'check_in_at' => now()->setHour(8)->setMinute(0),
        'check_out_at' => now()->setHour(17)->setMinute(0),
    ]);

    $response = $this->get(route('admin.hr.attendances.index'));
    $response->assertOk();
    $response->assertInertia(fn (Assert $page) => $page
        ->component('admin/hr/attendance/index')
        ->has('attendances.data', 1)
        ->where('attendances.total', 1)
        ->where('attendances.current_page', 1)
        ->where('attendances.per_page', 25)
    );
});

test('index page works with zero attendances', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $response = $this->get(route('admin.hr.attendances.index'));
    $response->assertOk();
    $response->assertInertia(fn (Assert $page) => $page
        ->component('admin/hr/attendance/index')
        ->has('attendances.data', 0)
        ->where('attendances.total', 0)
        ->where('attendances.current_page', 1)
        ->where('attendances.per_page', 25)
    );
});

test('index page passes current year and month', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $response = $this->get(route('admin.hr.attendances.index'));
    $response->assertOk();
    $response->assertInertia(fn (Assert $page) => $page
        ->where('currentYear', now()->year)
        ->where('currentMonth', now()->month)
    );
});

test('index page can filter by year and month', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $employeeUser = User::factory()->create();
    $employeeUser->employee()->create([
        'join_date' => '2024-01-01',
        'employment_status' => 'full_time',
        'base_salary' => 5000000,
    ]);

    Attendance::factory()->create([
        'employee_id' => $employeeUser->employee->id,
        'attendance_date' => '2024-03-15',
        'check_in_at' => '2024-03-15 08:00:00',
        'check_out_at' => '2024-03-15 17:00:00',
    ]);

    $response = $this->get(route('admin.hr.attendances.index', ['year' => 2024, 'month' => 3]));
    $response->assertOk();
    $response->assertInertia(fn (Assert $page) => $page
        ->where('currentYear', 2024)
        ->where('currentMonth', 3)
        ->has('attendances.data', 1)
        ->where('attendances.total', 1)
        ->where('attendances.current_page', 1)
        ->where('attendances.per_page', 25)
    );
});

test('index page excludes attendances from other months', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $employeeUser = User::factory()->create();
    $employeeUser->employee()->create([
        'join_date' => '2024-01-01',
        'employment_status' => 'full_time',
        'base_salary' => 5000000,
    ]);

    Attendance::factory()->create([
        'employee_id' => $employeeUser->employee->id,
        'attendance_date' => '2024-03-15',
        'check_in_at' => '2024-03-15 08:00:00',
    ]);

    $response = $this->get(route('admin.hr.attendances.index', ['year' => 2024, 'month' => 4]));
    $response->assertOk();
    $response->assertInertia(fn (Assert $page) => $page
        ->has('attendances.data', 0)
        ->where('attendances.total', 0)
        ->where('attendances.current_page', 1)
        ->where('attendances.per_page', 25)
    );
});

test('index page passes hrSettings props', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $response = $this->get(route('admin.hr.attendances.index'));
    $response->assertOk();
    $response->assertInertia(fn (Assert $page) => $page
        ->has('hrSettings.scheduled_check_in_time')
        ->has('hrSettings.scheduled_check_out_time')
    );
});

test('index page passes monthStats', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $response = $this->get(route('admin.hr.attendances.index'));
    $response->assertOk();
    $response->assertInertia(fn (Assert $page) => $page
        ->has('monthStats.working_days')
        ->has('monthStats.present')
        ->has('monthStats.absent')
        ->has('monthStats.leave')
    );
});

test('index page passes todayAttendance as null when no attendance today', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $response = $this->get(route('admin.hr.attendances.index'));
    $response->assertOk();
    $response->assertInertia(fn (Assert $page) => $page
        ->where('todayAttendance', null)
    );
});

/*
|--------------------------------------------------------------------------
| STORE / CHECK-IN
|--------------------------------------------------------------------------
*/

test('guest cannot check in', function () {
    $response = $this->post(route('admin.hr.attendances.store'), [
        'photo' => 'data:image/jpeg;base64,'.base64_encode('fake-image'),
        'latitude' => -6.2088,
        'longitude' => 106.8456,
    ]);

    $response->assertRedirect(route('login'));
});

test('authenticated user without employee cannot check in', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $response = $this->post(route('admin.hr.attendances.store'), [
        'photo' => 'data:image/jpeg;base64,'.base64_encode('fake-image'),
        'latitude' => -6.2088,
        'longitude' => 106.8456,
    ]);

    $response->assertRedirect();
});

test('employee can check in successfully', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $employee = Employee::factory()->create([
        'user_id' => $user->id,
        'join_date' => now()->subMonth()->toDateString(),
        'employment_status' => 'full_time',
        'base_salary' => 5000000,
    ]);

    $response = $this->post(route('admin.hr.attendances.store'), [
        'photo' => 'data:image/jpeg;base64,'.base64_encode('fake-image'),
        'latitude' => -6.2088,
        'longitude' => 106.8456,
    ]);

    $response->assertRedirect();

    $this->assertDatabaseHas('attendances', [
        'employee_id' => $employee->id,
        'attendance_date' => now()->toDateString(),
    ]);
});

test('employee cannot check in twice on the same day', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $employee = Employee::factory()->create([
        'user_id' => $user->id,
        'join_date' => now()->subMonth()->toDateString(),
        'employment_status' => 'full_time',
        'base_salary' => 5000000,
    ]);

    Attendance::factory()->create([
        'employee_id' => $employee->id,
        'attendance_date' => now()->toDateString(),
        'check_in_at' => now(),
    ]);

    $response = $this->post(route('admin.hr.attendances.store'), [
        'photo' => 'data:image/jpeg;base64,'.base64_encode('fake-image'),
        'latitude' => -6.2088,
        'longitude' => 106.8456,
    ]);

    $response->assertRedirect();
    $this->assertDatabaseCount('attendances', 1);
});

test('check in requires photo', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    Employee::factory()->create([
        'user_id' => $user->id,
        'join_date' => now()->subMonth()->toDateString(),
        'employment_status' => 'full_time',
        'base_salary' => 5000000,
    ]);

    $response = $this->post(route('admin.hr.attendances.store'), [
        'latitude' => -6.2088,
        'longitude' => 106.8456,
    ]);

    $response->assertRedirect();
    $this->assertDatabaseCount('attendances', 0);
});

test('check in requires latitude', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    Employee::factory()->create([
        'user_id' => $user->id,
        'join_date' => now()->subMonth()->toDateString(),
        'employment_status' => 'full_time',
        'base_salary' => 5000000,
    ]);

    $response = $this->post(route('admin.hr.attendances.store'), [
        'photo' => 'data:image/jpeg;base64,'.base64_encode('fake-image'),
        'longitude' => 106.8456,
    ]);

    $response->assertRedirect();
    $this->assertDatabaseCount('attendances', 0);
});

test('check in requires longitude', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    Employee::factory()->create([
        'user_id' => $user->id,
        'join_date' => now()->subMonth()->toDateString(),
        'employment_status' => 'full_time',
        'base_salary' => 5000000,
    ]);

    $response = $this->post(route('admin.hr.attendances.store'), [
        'photo' => 'data:image/jpeg;base64,'.base64_encode('fake-image'),
        'latitude' => -6.2088,
    ]);

    $response->assertRedirect();
    $this->assertDatabaseCount('attendances', 0);
});

test('check in latitude must be numeric', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    Employee::factory()->create([
        'user_id' => $user->id,
        'join_date' => now()->subMonth()->toDateString(),
        'employment_status' => 'full_time',
        'base_salary' => 5000000,
    ]);

    $response = $this->post(route('admin.hr.attendances.store'), [
        'photo' => 'data:image/jpeg;base64,'.base64_encode('fake-image'),
        'latitude' => 'not-a-number',
        'longitude' => 106.8456,
    ]);

    $response->assertRedirect();
    $this->assertDatabaseCount('attendances', 0);
});

test('check in longitude must be numeric', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    Employee::factory()->create([
        'user_id' => $user->id,
        'join_date' => now()->subMonth()->toDateString(),
        'employment_status' => 'full_time',
        'base_salary' => 5000000,
    ]);

    $response = $this->post(route('admin.hr.attendances.store'), [
        'photo' => 'data:image/jpeg;base64,'.base64_encode('fake-image'),
        'latitude' => -6.2088,
        'longitude' => 'not-a-number',
    ]);

    $response->assertRedirect();
    $this->assertDatabaseCount('attendances', 0);
});

/*
|--------------------------------------------------------------------------
| UPDATE / CHECK-OUT
|--------------------------------------------------------------------------
*/

test('guest cannot check out', function () {
    $attendance = Attendance::factory()->create([
        'check_in_at' => now()->subHours(8),
        'check_out_at' => null,
    ]);

    $response = $this->put(route('admin.hr.attendances.update', $attendance), [
        'photo' => 'data:image/jpeg;base64,'.base64_encode('fake-image'),
        'latitude' => -6.2088,
        'longitude' => 106.8456,
    ]);

    $response->assertRedirect(route('login'));
});

test('employee can check out successfully', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $employee = Employee::factory()->create([
        'user_id' => $user->id,
        'join_date' => now()->subMonth()->toDateString(),
        'employment_status' => 'full_time',
        'base_salary' => 5000000,
    ]);

    $attendance = Attendance::factory()->create([
        'employee_id' => $employee->id,
        'attendance_date' => now()->toDateString(),
        'check_in_at' => now()->subHours(8),
        'check_out_at' => null,
    ]);

    $response = $this->put(route('admin.hr.attendances.update', $attendance), [
        'photo' => 'data:image/jpeg;base64,'.base64_encode('fake-image'),
        'latitude' => -6.2088,
        'longitude' => 106.8456,
    ]);

    $response->assertRedirect();

    $attendance->refresh();
    $this->assertNotNull($attendance->check_out_at);
    $this->assertEquals(-6.2088, $attendance->check_out_latitude);
    $this->assertEquals(106.8456, $attendance->check_out_longitude);
});

test('check out requires photo', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $employee = Employee::factory()->create([
        'user_id' => $user->id,
        'join_date' => now()->subMonth()->toDateString(),
        'employment_status' => 'full_time',
        'base_salary' => 5000000,
    ]);

    $attendance = Attendance::factory()->create([
        'employee_id' => $employee->id,
        'attendance_date' => now()->toDateString(),
        'check_in_at' => now()->subHours(8),
        'check_out_at' => null,
    ]);

    $response = $this->put(route('admin.hr.attendances.update', $attendance), [
        'latitude' => -6.2088,
        'longitude' => 106.8456,
    ]);

    $response->assertRedirect();

    $attendance->refresh();
    $this->assertNull($attendance->check_out_at);
});

test('check out requires latitude', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $employee = Employee::factory()->create([
        'user_id' => $user->id,
        'join_date' => now()->subMonth()->toDateString(),
        'employment_status' => 'full_time',
        'base_salary' => 5000000,
    ]);

    $attendance = Attendance::factory()->create([
        'employee_id' => $employee->id,
        'attendance_date' => now()->toDateString(),
        'check_in_at' => now()->subHours(8),
        'check_out_at' => null,
    ]);

    $response = $this->put(route('admin.hr.attendances.update', $attendance), [
        'photo' => 'data:image/jpeg;base64,'.base64_encode('fake-image'),
        'longitude' => 106.8456,
    ]);

    $response->assertRedirect();

    $attendance->refresh();
    $this->assertNull($attendance->check_out_at);
});

test('check out requires longitude', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $employee = Employee::factory()->create([
        'user_id' => $user->id,
        'join_date' => now()->subMonth()->toDateString(),
        'employment_status' => 'full_time',
        'base_salary' => 5000000,
    ]);

    $attendance = Attendance::factory()->create([
        'employee_id' => $employee->id,
        'attendance_date' => now()->toDateString(),
        'check_in_at' => now()->subHours(8),
        'check_out_at' => null,
    ]);

    $response = $this->put(route('admin.hr.attendances.update', $attendance), [
        'photo' => 'data:image/jpeg;base64,'.base64_encode('fake-image'),
        'latitude' => -6.2088,
    ]);

    $response->assertRedirect();

    $attendance->refresh();
    $this->assertNull($attendance->check_out_at);
});

test('updating non-existent attendance returns 404', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $response = $this->put(route('admin.hr.attendances.update', 999999), [
        'photo' => 'data:image/jpeg;base64,'.base64_encode('fake-image'),
        'latitude' => -6.2088,
        'longitude' => 106.8456,
    ]);

    $response->assertStatus(404);
});

/*
|--------------------------------------------------------------------------
| BY-DATE
|--------------------------------------------------------------------------
*/

test('guest cannot access by-date endpoint', function () {
    $response = $this->get(route('admin.hr.attendances.by-date'));
    $response->assertRedirect(route('login'));
});

test('by-date returns empty when no attendance for date', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $response = $this->getJson(route('admin.hr.attendances.by-date', ['date' => '2024-01-01']));
    $response->assertOk();
});

test('by-date defaults to today when no date parameter', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $response = $this->getJson(route('admin.hr.attendances.by-date'));
    $response->assertOk();
});

/*
|--------------------------------------------------------------------------
| MONTH STATS
|--------------------------------------------------------------------------
*/

test('month stats calculates correct working days for january 2024', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $response = $this->get(route('admin.hr.attendances.index', ['year' => 2024, 'month' => 1]));
    $response->assertOk();
    $response->assertInertia(fn (Assert $page) => $page
        ->where('monthStats.working_days', 23)
    );
});

test('month stats present count matches attendance records', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $employeeUser = User::factory()->create();
    $employeeUser->employee()->create([
        'join_date' => '2024-01-01',
        'employment_status' => 'full_time',
        'base_salary' => 5000000,
    ]);

    Attendance::factory()->create([
        'employee_id' => $employeeUser->employee->id,
        'attendance_date' => '2024-01-15',
        'check_in_at' => '2024-01-15 08:00:00',
    ]);

    Attendance::factory()->create([
        'employee_id' => $employeeUser->employee->id,
        'attendance_date' => '2024-01-16',
        'check_in_at' => '2024-01-16 08:00:00',
    ]);

    $response = $this->get(route('admin.hr.attendances.index', ['year' => 2024, 'month' => 1]));
    $response->assertOk();
    $response->assertInertia(fn (Assert $page) => $page
        ->where('monthStats.present', 2)
    );
});

/*
|--------------------------------------------------------------------------
| MULTIPLE ATTENDANCES
|--------------------------------------------------------------------------
*/

test('multiple employees can have attendances on the same day', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $emp1User = User::factory()->create();
    $emp1User->employee()->create([
        'join_date' => '2024-01-01',
        'employment_status' => 'full_time',
        'base_salary' => 5000000,
    ]);

    $emp2User = User::factory()->create();
    $emp2User->employee()->create([
        'join_date' => '2024-01-01',
        'employment_status' => 'full_time',
        'base_salary' => 5000000,
    ]);

    Attendance::factory()->create([
        'employee_id' => $emp1User->employee->id,
        'attendance_date' => '2024-01-15',
        'check_in_at' => '2024-01-15 08:00:00',
    ]);

    Attendance::factory()->create([
        'employee_id' => $emp2User->employee->id,
        'attendance_date' => '2024-01-15',
        'check_in_at' => '2024-01-15 08:30:00',
    ]);

    $response = $this->get(route('admin.hr.attendances.index', ['year' => 2024, 'month' => 1]));
    $response->assertOk();
    $response->assertInertia(fn (Assert $page) => $page
        ->has('attendances.data', 2)
        ->where('attendances.total', 2)
        ->where('attendances.current_page', 1)
        ->where('attendances.per_page', 25)
    );
});

test('employee can have multiple attendances across different days', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $employeeUser = User::factory()->create();
    $employeeUser->employee()->create([
        'join_date' => '2024-01-01',
        'employment_status' => 'full_time',
        'base_salary' => 5000000,
    ]);

    Attendance::factory()->create([
        'employee_id' => $employeeUser->employee->id,
        'attendance_date' => '2024-01-15',
        'check_in_at' => '2024-01-15 08:00:00',
    ]);

    Attendance::factory()->create([
        'employee_id' => $employeeUser->employee->id,
        'attendance_date' => '2024-01-16',
        'check_in_at' => '2024-01-16 08:00:00',
    ]);

    Attendance::factory()->create([
        'employee_id' => $employeeUser->employee->id,
        'attendance_date' => '2024-01-17',
        'check_in_at' => '2024-01-17 08:00:00',
    ]);

    $response = $this->get(route('admin.hr.attendances.index', ['year' => 2024, 'month' => 1]));
    $response->assertOk();
    $response->assertInertia(fn (Assert $page) => $page
        ->has('attendances.data', 3)
        ->where('attendances.total', 3)
        ->where('attendances.current_page', 1)
        ->where('attendances.per_page', 25)
    );
});

/*
|--------------------------------------------------------------------------
| DATA INTEGRITY
|--------------------------------------------------------------------------
*/

test('attendance stores correct check-in coordinates', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $employee = Employee::factory()->create([
        'user_id' => $user->id,
        'join_date' => now()->subMonth()->toDateString(),
        'employment_status' => 'full_time',
        'base_salary' => 5000000,
    ]);

    $this->post(route('admin.hr.attendances.store'), [
        'photo' => 'data:image/jpeg;base64,'.base64_encode('fake-image'),
        'latitude' => -6.2088,
        'longitude' => 106.8456,
    ]);

    $attendance = Attendance::where('employee_id', $employee->id)->first();
    $this->assertEquals(-6.2088, $attendance->check_in_latitude);
    $this->assertEquals(106.8456, $attendance->check_in_longitude);
});

test('attendance has correct attendance_date', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $employee = Employee::factory()->create([
        'user_id' => $user->id,
        'join_date' => now()->subMonth()->toDateString(),
        'employment_status' => 'full_time',
        'base_salary' => 5000000,
    ]);

    $this->post(route('admin.hr.attendances.store'), [
        'photo' => 'data:image/jpeg;base64,'.base64_encode('fake-image'),
        'latitude' => -6.2088,
        'longitude' => 106.8456,
    ]);

    $this->assertDatabaseHas('attendances', [
        'employee_id' => $employee->id,
        'attendance_date' => now()->toDateString(),
    ]);
});

test('check-in sets check_out_at to null', function () {
    $user = User::factory()->create();
    $this->actingAs($user);

    $employee = Employee::factory()->create([
        'user_id' => $user->id,
        'join_date' => now()->subMonth()->toDateString(),
        'employment_status' => 'full_time',
        'base_salary' => 5000000,
    ]);

    $this->post(route('admin.hr.attendances.store'), [
        'photo' => 'data:image/jpeg;base64,'.base64_encode('fake-image'),
        'latitude' => -6.2088,
        'longitude' => 106.8456,
    ]);

    $attendance = Attendance::where('employee_id', $employee->id)->first();
    $this->assertNull($attendance->check_out_at);
});
