<?php

namespace Tests\Feature;

use App\Models\Attendance;
use App\Models\OfficeLocation;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class QrAttendanceValidationTest extends TestCase
{
    use RefreshDatabase;

    protected function setUp(): void
    {
        parent::setUp();
        $this->seed();
    }

    public function test_employee_can_view_scan_qr_page(): void
    {
        $employeeUser = User::where('email', 'employee@absensi.test')->first();

        $response = $this->actingAs($employeeUser)->get('/employee/scan-qr');
        $response->assertStatus(200);
        $response->assertSee('Scanner QR Absensi');
    }

    public function test_admin_can_view_qr_attendance_generator_page(): void
    {
        $admin = User::where('role', 'super_admin')->first();

        $response = $this->actingAs($admin)->get('/admin/qr-attendance');
        $response->assertStatus(200);
        $response->assertSee('QR Code Absensi');
    }

    public function test_qr_scan_approved_when_within_radius_and_good_gps_accuracy(): void
    {
        $employeeUser = User::where('email', 'joko.widodo@absensi.test')->first();
        $office = OfficeLocation::first();
        $office->update([
            'radius_meters' => 20,
            'max_gps_accuracy' => 30,
        ]);

        // Exact office coordinates, good accuracy (10 meters)
        $response = $this->actingAs($employeeUser)->postJson('/employee/attendance/scan-qr', [
            'qr_code' => "ABSENSI-OFFICE-{$office->id}",
            'latitude' => $office->latitude,
            'longitude' => $office->longitude,
            'accuracy' => 10.5,
        ]);

        $response->assertStatus(200);
        $response->assertJson([
            'success' => true,
            'type' => 'check_in',
        ]);

        $this->assertDatabaseHas('attendances', [
            'employee_id' => $employeeUser->employee->id,
            'office_location_id' => $office->id,
            'accuracy_in' => 10.5,
        ]);
    }

    public function test_qr_scan_rejected_when_outside_radius(): void
    {
        $employeeUser = User::where('email', 'joko.widodo@absensi.test')->first();
        $office = OfficeLocation::first();
        $office->update([
            'radius_meters' => 10,
        ]);

        // Far coordinates (e.g. 500 meters away)
        $response = $this->actingAs($employeeUser)->postJson('/employee/attendance/scan-qr', [
            'qr_code' => "ABSENSI-OFFICE-{$office->id}",
            'latitude' => $office->latitude + 0.005,
            'longitude' => $office->longitude + 0.005,
            'accuracy' => 10.0,
        ]);

        $response->assertStatus(422);
        $response->assertJson([
            'success' => false,
            'error_type' => 'radius',
        ]);
    }

    public function test_qr_scan_rejected_when_gps_accuracy_is_too_low(): void
    {
        $employeeUser = User::where('email', 'joko.widodo@absensi.test')->first();
        $office = OfficeLocation::first();
        $office->update([
            'max_gps_accuracy' => 30,
            'radius_meters' => 100,
        ]);

        // Accuracy is 85 meters (exceeds max_gps_accuracy 30m)
        $response = $this->actingAs($employeeUser)->postJson('/employee/attendance/scan-qr', [
            'qr_code' => "ABSENSI-OFFICE-{$office->id}",
            'latitude' => $office->latitude,
            'longitude' => $office->longitude,
            'accuracy' => 85.0,
        ]);

        $response->assertStatus(422);
        $response->assertJson([
            'success' => false,
            'error_type' => 'accuracy',
        ]);
    }

    public function test_dynamic_radius_update_by_admin_controls_validation_immediately(): void
    {
        $employeeUser = User::where('email', 'joko.widodo@absensi.test')->first();
        $office = OfficeLocation::first();

        // 1. Set radius to 5 meters
        $office->update(['radius_meters' => 5, 'max_gps_accuracy' => 50]);

        // Employee is ~15 meters away
        $testLat = $office->latitude + 0.00013;
        $testLng = $office->longitude;

        $rejectedResponse = $this->actingAs($employeeUser)->postJson('/employee/attendance/scan-qr', [
            'qr_code' => "ABSENSI-OFFICE-{$office->id}",
            'latitude' => $testLat,
            'longitude' => $testLng,
            'accuracy' => 10.0,
        ]);

        $rejectedResponse->assertStatus(422);

        // 2. Admin increases radius to 50 meters
        $office->update(['radius_meters' => 50]);

        $approvedResponse = $this->actingAs($employeeUser)->postJson('/employee/attendance/scan-qr', [
            'qr_code' => "ABSENSI-OFFICE-{$office->id}",
            'latitude' => $testLat,
            'longitude' => $testLng,
            'accuracy' => 10.0,
        ]);

        $approvedResponse->assertStatus(200);
        $approvedResponse->assertJson([
            'success' => true,
        ]);
    }

    public function test_second_qr_scan_records_checkout(): void
    {
        $employeeUser = User::where('email', 'joko.widodo@absensi.test')->first();
        $office = OfficeLocation::first();
        $office->update(['radius_meters' => 50, 'max_gps_accuracy' => 50]);

        // 1st Scan: Check-In
        $this->actingAs($employeeUser)->postJson('/employee/attendance/scan-qr', [
            'qr_code' => "ABSENSI-OFFICE-{$office->id}",
            'latitude' => $office->latitude,
            'longitude' => $office->longitude,
            'accuracy' => 10.0,
        ]);

        // 2nd Scan: Check-Out
        $response = $this->actingAs($employeeUser)->postJson('/employee/attendance/scan-qr', [
            'qr_code' => "ABSENSI-OFFICE-{$office->id}",
            'latitude' => $office->latitude,
            'longitude' => $office->longitude,
            'accuracy' => 12.0,
        ]);

        $response->assertStatus(200);
        $response->assertJson([
            'success' => true,
            'type' => 'check_out',
        ]);

        $attendance = Attendance::where('employee_id', $employeeUser->employee->id)->latest('id')->first();
        $this->assertNotNull($attendance->check_in);
        $this->assertNotNull($attendance->check_out);
        $this->assertEquals(12.0, $attendance->accuracy_out);
    }

    public function test_office_location_generates_valid_scan_url_attribute(): void
    {
        $office = OfficeLocation::first();
        $scanUrl = $office->scan_url;

        $this->assertStringContainsString("/attend/qr/{$office->id}", $scanUrl);
        $this->assertStringContainsString("token=", $scanUrl);
    }

    public function test_direct_qr_url_renders_confirmation_page_for_employee(): void
    {
        $employeeUser = User::where('email', 'employee@absensi.test')->first();
        $office = OfficeLocation::first();

        $response = $this->actingAs($employeeUser)->get("/attend/qr/{$office->id}");
        $response->assertStatus(200);
        $response->assertSee('Verifikasi Lokasi GPS');
    }

    public function test_admin_live_stream_endpoint_returns_real_time_attendances(): void
    {
        $admin = User::where('role', 'super_admin')->first();

        $response = $this->actingAs($admin)->getJson('/admin/qr-attendance/live-stream');
        $response->assertStatus(200);
        $response->assertJsonStructure([
            'success',
            'attendances',
            'stats' => ['total_present', 'total_late']
        ]);
    }
}
