PXG_2026_API/app/Http/Controllers/Api/V1/PairingController.php

156 lines
5.7 KiB
PHP

<?php
namespace App\Http\Controllers\Api\V1;
use App\Http\Controllers\Controller;
use App\Http\Requests\PairingLookupRequest;
use App\Http\Requests\PairingOtpRequest;
use App\Http\Requests\PairingOtpVerifyRequest;
use App\Models\Registration;
use App\Services\OtpService;
use App\Services\PairingTokenService;
use Illuminate\Http\Request;
class PairingController extends Controller
{
public function lookup(PairingLookupRequest $request)
{
$code = $request->validated()['registration_code'];
$registration = Registration::where('registration_code', $code)
->with('player', 'flightMember.flight.members.registration.player')
->firstOrFail();
$flightMember = $registration->flightMember;
$flight = $flightMember?->flight;
$members = [];
if ($flight) {
foreach ($flight->members as $m) {
$members[] = [
'seat' => (int) $m->seat_no,
'name' => $m->registration?->player?->name,
'band' => $m->registration?->handicap_band,
'hcp' => $m->registration?->handicap_value,
'is_you' => $m->registration_id === $registration->id,
];
}
}
return response()->json([
'registration' => [
'status' => $registration->status,
'player' => [
'name' => $registration->player->name,
'band' => $registration->handicap_band,
'handicap' => $registration->handicap_value,
],
'preferences' => [
'pairing_mode' => $registration->pairing_mode,
'pairing_mode_label' => strtolower($registration->pairing_mode) === 'LEVEL' ? 'level' : (strtolower($registration->pairing_mode) === 'RANDOM' ? 'random' : 'balanced'),
],
],
'flight' => $flight ? [
'code' => $flight->code,
'course' => $flight->course,
'start_hole' => (int) $flight->start_hole,
'start_tee' => $flight->start_tee,
'is_final' => $flight->status === 'FINAL',
] : null,
'members' => $members,
]);
}
public function requestOtp(PairingOtpRequest $request, OtpService $otpService)
{
$registrationId = $request->validated()['registration_id'];
$registration = Registration::with('player')->findOrFail($registrationId);
$otpService->sendPairingOtp($registration);
return response()->json(['ok' => true]);
}
public function verifyOtp(PairingOtpVerifyRequest $request, OtpService $otpService, PairingTokenService $tokenService)
{
$data = $request->validated();
$registration = Registration::with('player')->findOrFail($data['registration_id']);
$otpService->verifyPairingOtp($registration, $data['otp']);
$token = $tokenService->issue($registration->id);
return response()->json([
'pairing_token' => $token,
]);
}
public function me(Request $request, PairingTokenService $tokenService)
{
$auth = $request->header('Authorization', '');
if (!str_starts_with($auth, 'Bearer ')) {
return response()->json(['message' => 'Unauthorized'], 401);
}
$token = trim(substr($auth, 7));
$registrationId = $tokenService->verify($token);
if (!$registrationId) {
return response()->json(['message' => 'Unauthorized'], 401);
}
$registration = Registration::with('player', 'flightMember.flight.members.registration.player')
->findOrFail($registrationId);
$flightMember = $registration->flightMember;
$flight = $flightMember?->flight;
// Build members array for diagram (seat 1..4)
$members = [];
if ($flight) {
foreach ($flight->members as $m) {
$members[] = [
'seat' => (int) $m->seat_no,
'name' => $m->registration?->player?->name,
'band' => $m->registration?->handicap_band,
'hcp' => $m->registration?->handicap_value,
'is_you' => $m->registration_id === $registration->id,
];
}
}
return response()->json([
'registration' => [
'status' => $registration->status,
'player' => [
'name' => $registration->player->name,
'band' => $registration->handicap_band,
'handicap' => $registration->handicap_value,
],
// Optional for UI copy; can be expanded later:
'preferences' => [
'pairing_mode' => $registration->pairing_mode,
'pairing_mode_label' => strtolower($registration->pairing_mode) === 'LEVEL' ? 'level' : (strtolower($registration->pairing_mode) === 'RANDOM' ? 'random' : 'balanced'),
],
],
'flight' => $flight ? [
'code' => $flight->code,
'course' => $flight->course,
'start_hole' => (int) $flight->start_hole,
'start_tee' => $flight->start_tee,
'is_final' => $flight->status === 'FINAL',
] : null,
'members' => $members,
]);
}
private function maskPhone(string $phone): string
{
// crude masking; adjust as needed for E164 normalization
$digits = preg_replace('/\D+/', '', $phone);
if (strlen($digits) <= 4) return '+**' . $digits;
$last4 = substr($digits, -4);
return '+'.substr($digits, 0, 2) . '***' . $last4;
}
}