<?php

namespace Tests\Feature\Api;

use App\Models\AttendanceEvent;
use App\Models\Device;
use App\Models\Employee;
use App\Models\Site;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Str;
use Tests\TestCase;

class PunchesTest extends TestCase
{
    use RefreshDatabase;

    protected function makeDevice(): array
    {
        $site = Site::factory()->create();
        $token = Str::random(64);
        $device = Device::factory()->create([
            'site_id' => $site->id,
            'token_hash' => hash('sha256', $token),
        ]);

        return [$device, $token];
    }

    public function test_batch_punch_upload_is_idempotent_on_resend(): void
    {
        [, $token] = $this->makeDevice();
        Employee::factory()->create(['emp_id' => 'EMP0001']);

        $uuid = (string) Str::uuid();
        $payload = [
            'events' => [[
                'event_uuid' => $uuid,
                'emp_id' => 'EMP0001',
                'direction' => 'IN',
                'ts' => now()->toIso8601String(),
                'confidence' => 0.9,
                'liveness_pass' => true,
                'source' => 'face',
                'direction_confidence' => 0.9,
            ]],
        ];

        $response1 = $this->withHeaders(['X-Device-Token' => $token])->postJson('/api/v1/punches', $payload);
        $response1->assertOk();
        $response1->assertJson(['accepted' => [$uuid], 'duplicate' => [], 'rejected' => []]);

        $this->assertEquals(1, AttendanceEvent::count());

        $response2 = $this->withHeaders(['X-Device-Token' => $token])->postJson('/api/v1/punches', $payload);
        $response2->assertOk();
        $response2->assertJson(['accepted' => [], 'duplicate' => [$uuid], 'rejected' => []]);

        $this->assertEquals(1, AttendanceEvent::count());
    }

    public function test_unknown_emp_id_is_rejected_rest_processed(): void
    {
        [, $token] = $this->makeDevice();
        Employee::factory()->create(['emp_id' => 'EMP0001']);

        $goodUuid = (string) Str::uuid();
        $badUuid = (string) Str::uuid();

        $payload = [
            'events' => [
                ['event_uuid' => $badUuid, 'emp_id' => 'EMP9999', 'direction' => 'IN', 'ts' => now()->toIso8601String(), 'confidence' => 0.9, 'liveness_pass' => true, 'source' => 'face', 'direction_confidence' => 0.9],
                ['event_uuid' => $goodUuid, 'emp_id' => 'EMP0001', 'direction' => 'IN', 'ts' => now()->toIso8601String(), 'confidence' => 0.9, 'liveness_pass' => true, 'source' => 'face', 'direction_confidence' => 0.9],
            ],
        ];

        $response = $this->withHeaders(['X-Device-Token' => $token])->postJson('/api/v1/punches', $payload);
        $response->assertOk();
        $response->assertJson([
            'accepted' => [$goodUuid],
            'duplicate' => [],
            'rejected' => [['event_uuid' => $badUuid, 'reason' => 'unknown_emp_id']],
        ]);

        $this->assertEquals(1, AttendanceEvent::count());
    }

    public function test_corrects_event_uuid_marks_original_as_corrected(): void
    {
        [, $token] = $this->makeDevice();
        Employee::factory()->create(['emp_id' => 'EMP0001']);

        $originalUuid = (string) Str::uuid();
        $this->withHeaders(['X-Device-Token' => $token])->postJson('/api/v1/punches', [
            'events' => [['event_uuid' => $originalUuid, 'emp_id' => 'EMP0001', 'direction' => 'IN', 'ts' => now()->toIso8601String(), 'confidence' => 0.5, 'liveness_pass' => true, 'source' => 'face', 'direction_confidence' => 0.3]],
        ])->assertOk();

        $correctionUuid = (string) Str::uuid();
        $this->withHeaders(['X-Device-Token' => $token])->postJson('/api/v1/punches', [
            'events' => [[
                'event_uuid' => $correctionUuid,
                'emp_id' => 'EMP0001',
                'direction' => 'OUT',
                'ts' => now()->toIso8601String(),
                'confidence' => 1.0,
                'liveness_pass' => true,
                'source' => 'manual',
                'direction_confidence' => 1.0,
                'corrects_event_uuid' => $originalUuid,
            ]],
        ])->assertOk();

        $original = AttendanceEvent::where('event_uuid', $originalUuid)->first();
        $correction = AttendanceEvent::where('event_uuid', $correctionUuid)->first();

        $this->assertNotNull($original->corrected_by);
        $this->assertEquals($correction->id, $original->corrected_by);
        $this->assertEquals($original->id, $correction->corrects_event_id);
    }

    public function test_batch_over_200_events_is_rejected(): void
    {
        [, $token] = $this->makeDevice();

        $events = [];
        for ($i = 0; $i < 201; $i++) {
            $events[] = ['event_uuid' => (string) Str::uuid(), 'emp_id' => 'EMP0001', 'direction' => 'IN', 'ts' => now()->toIso8601String()];
        }

        $response = $this->withHeaders(['X-Device-Token' => $token])->postJson('/api/v1/punches', ['events' => $events]);
        $response->assertStatus(422);
    }
}
