<?php

namespace Tests\Feature;

use App\Models\AttendanceLog;
use App\Models\FingerprintMachine;
use App\Models\Role;
use App\Models\User;
use App\Services\Fingerprint\Cloud\FingerspotWebhookService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class MultiMachineIsolationTest extends TestCase
{
    use RefreshDatabase;

    protected function setUp(): void
    {
        parent::setUp();
        Role::create(['name' => 'siswa', 'label' => 'Siswa']);
        User::factory()->create(['nip_nis' => '1001', 'is_active' => true]);
    }

    public function test_six_machines_can_ingest_same_user_scan_concurrently_without_collision(): void
    {
        $machines = [];
        for ($i = 1; $i <= 6; $i++) {
            $machines[$i] = FingerprintMachine::create([
                'name' => "Revo W-231N Unit #{$i}",
                'ip_address' => "192.168.0.10{$i}",
                'port' => '4370',
                'connection_type' => 'local_connector',
                'protocol' => 'local_connector',
                'cloud_id' => "TEST-REVO-00{$i}",
                'connector_token' => "SECRET_TOKEN_M{$i}",
                'status' => 'online',
            ]);
        }

        // Push attendance log for user 1001 at 07:15:00 to ALL 6 machines
        foreach ($machines as $i => $machine) {
            $payload = [
                'machine_id' => $machine->id,
                'ip_address' => $machine->ip_address,
                'serial_number' => $machine->cloud_id,
                'logs' => [
                    [
                        'pin' => '1001',
                        'scan' => '2026-08-09 07:15:00',
                        'verify' => 'fingerprint',
                    ],
                ],
            ];

            $response = $this->withHeaders([
                'X-Connector-Token' => "SECRET_TOKEN_M{$i}",
            ])->postJson('/api/connector/push-attendance', $payload);

            $response->assertStatus(200);
            $response->assertJson([
                'success' => true,
                'received' => 1,
                'inserted' => 1,
                'duplicates' => 0,
            ]);
        }

        // Verify exactly 6 distinct attendance logs exist in DB (1 per machine)
        $this->assertEquals(6, AttendanceLog::where('raw_user_id', '1001')->count());

        for ($i = 1; $i <= 6; $i++) {
            $this->assertDatabaseHas('attendance_logs', [
                'machine_id' => $machines[$i]->id,
                'raw_user_id' => '1001',
                'timestamp' => '2026-08-09 07:15:00',
            ]);
        }
    }

    public function test_machine_identity_and_token_authorization_cases(): void
    {
        $m1 = FingerprintMachine::create([
            'name' => 'Machine 1',
            'ip_address' => '192.168.0.101',
            'cloud_id' => 'TEST-REVO-001',
            'connector_token' => 'TOKEN_MACHINE_1',
        ]);

        $m2 = FingerprintMachine::create([
            'name' => 'Machine 2',
            'ip_address' => '192.168.0.102',
            'cloud_id' => 'TEST-REVO-002',
            'connector_token' => 'TOKEN_MACHINE_2',
        ]);

        // CASE 1: Machine 1 + Token 1 -> PASS (200)
        $res1 = $this->postJson('/api/connector/heartbeat', [
            'machine_id' => $m1->id,
            'serial_number' => 'TEST-REVO-001',
        ], ['X-Connector-Token' => 'TOKEN_MACHINE_1']);
        $res1->assertStatus(200);

        // CASE 2: Machine 1 + Token 2 -> FAIL (401)
        $res2 = $this->postJson('/api/connector/heartbeat', [
            'machine_id' => $m1->id,
            'serial_number' => 'TEST-REVO-001',
        ], ['X-Connector-Token' => 'TOKEN_MACHINE_2']);
        $res2->assertStatus(401);

        // CASE 3: Machine 1 ID + Serial Machine 2 -> FAIL (404)
        $res3 = $this->postJson('/api/connector/heartbeat', [
            'machine_id' => $m1->id,
            'serial_number' => 'TEST-REVO-002',
        ], ['X-Connector-Token' => 'TOKEN_MACHINE_1']);
        $res3->assertStatus(404);

        // CASE 4: Missing Token -> FAIL (401)
        $res4 = $this->postJson('/api/connector/heartbeat', [
            'machine_id' => $m1->id,
            'serial_number' => 'TEST-REVO-001',
        ]);
        $res4->assertStatus(401);
    }

    public function test_cloud_webhook_and_local_connector_idempotency_collision(): void
    {
        $machine = FingerprintMachine::create([
            'name' => 'Hybrid Machine',
            'ip_address' => '192.168.0.101',
            'cloud_id' => 'TEST-HYBRID-001',
            'connector_token' => 'HYBRID_TOKEN',
        ]);

        // 1. Ingest via Local Connector
        $resLocal = $this->postJson('/api/connector/push-attendance', [
            'machine_id' => $machine->id,
            'serial_number' => 'TEST-HYBRID-001',
            'logs' => [
                ['pin' => '1001', 'scan' => '2026-08-09 08:00:00', 'verify' => 'fingerprint']
            ],
        ], ['X-Connector-Token' => 'HYBRID_TOKEN']);
        $resLocal->assertStatus(200);

        // 2. Simulate ADMS Push Ingest for exact same scan
        $admsRes = $this->call('POST', '/iclock/cdata', ['SN' => 'TEST-HYBRID-001', 'table' => 'ATTLOG'], [], [], [], "1001\t2026-08-09 08:00:00\t0\t1");
        $admsRes->assertStatus(200);

        // Assert exactly ONE log is in database (idempotency check)
        $this->assertEquals(1, AttendanceLog::where('raw_user_id', '1001')
            ->where('timestamp', '2026-08-09 08:00:00')
            ->count());
    }

    public function test_600_event_multi_machine_scale_performance(): void
    {
        $startTime = microtime(true);

        $machine1 = FingerprintMachine::create(['name' => 'M1', 'ip_address' => '1.1.1.1', 'cloud_id' => 'S1']);
        $machine2 = FingerprintMachine::create(['name' => 'M2', 'ip_address' => '1.1.1.2', 'cloud_id' => 'S2']);

        $logs1 = [];
        $logs2 = [];
        for ($i = 1; $i <= 300; $i++) {
            $logs1[] = ['pin' => (string) $i, 'scan' => '2026-08-09 07:00:00', 'verify' => 'fingerprint'];
            $logs2[] = ['pin' => (string) ($i + 300), 'scan' => '2026-08-09 07:00:00', 'verify' => 'fingerprint'];
        }

        $res1 = $this->postJson('/api/connector/push-attendance', [
            'machine_id' => $machine1->id,
            'serial_number' => 'S1',
            'logs' => $logs1,
        ]);
        $res1->assertStatus(200);

        $res2 = $this->postJson('/api/connector/push-attendance', [
            'machine_id' => $machine2->id,
            'serial_number' => 'S2',
            'logs' => $logs2,
        ]);
        $elapsed = microtime(true) - $startTime;
        $this->assertLessThan(15.0, $elapsed, "600 events bulk push should execute in reasonable time.");
    }
}
