<?php

namespace App\Modules\Integrations\Platform\Services;

use App\Modules\HR\Attendance\Repositories\BiometricDeviceRepository;
use App\Modules\HR\Attendance\Services\BiometricLogIngestService;
use App\Modules\Integrations\Platform\Models\ApiToken;
use App\Modules\Integrations\Platform\Requests\IngestBiometricPunchesRequest;
use App\Modules\Integrations\Platform\Support\ApiErrorResponse;
use App\Modules\Integrations\Platform\Support\ApiResponseFactory;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

final class BiometricIngestApiService
{
    public function __construct(
        private readonly ApiIdempotencyService $idempotency,
        private readonly BiometricDeviceRepository $devices,
        private readonly BiometricLogIngestService $ingest,
        private readonly ApiResponseFactory $responses,
    ) {}

    public function ingest(Request $request, IngestBiometricPunchesRequest $httpRequest, int $device): JsonResponse
    {
        $tenantId = (int) $request->attributes->get('tenant_id');
        if ($tenantId <= 0) {
            return $this->responses->error(new ApiErrorResponse('tenant.missing', 'Tenant context is required.'), 400);
        }

        $idem = (string) $request->headers->get('X-Idempotency-Key', '');
        if ($idem === '' || strlen($idem) > 128) {
            return $this->responses->error(
                new ApiErrorResponse('request.missing_idempotency_key', 'Missing X-Idempotency-Key header.'),
                400,
            );
        }

        /** @var ApiToken|null $token */
        $token = $request->attributes->get('api_token');
        $tokenId = $token instanceof ApiToken ? (int) $token->id : 0;
        if ($tokenId <= 0) {
            return $this->responses->error(new ApiErrorResponse('auth.invalid_token', 'Invalid API token.'), 401);
        }

        $rawBody = (string) $request->getContent();
        $idemState = $this->idempotency->begin($request, $tenantId, $tokenId, $idem, $rawBody);
        if ($idemState['state'] === 'conflict') {
            return $this->responses->error(
                new ApiErrorResponse('idempotency.key_mismatch', 'Idempotency key reused with a different request body.'),
                409,
            );
        }
        if ($idemState['state'] === 'in_flight') {
            return $this->responses->error(
                new ApiErrorResponse('idempotency.in_flight', 'Duplicate request while the original is still processing.'),
                409,
            );
        }
        if ($idemState['state'] === 'replay') {
            return response()->json(json_decode($idemState['body'], true), $idemState['status']);
        }

        $idemRowId = (int) $idemState['id'];

        $d = $this->devices->findForTenant($tenantId, $device);
        if ($d === null || ! (bool) $d->is_active) {
            $resp = $this->responses->error(new ApiErrorResponse('device.not_found', 'Device not found.'), 404);
            $this->idempotency->complete($idemRowId, 404, json_encode($resp->getData(), JSON_UNESCAPED_SLASHES));

            return $resp;
        }

        $n = $this->ingest->ingestAndQueue($d, (array) $httpRequest->validated('punches'), true);
        $ok = $this->responses->ok(['ingested' => $n]);
        $this->idempotency->complete($idemRowId, 200, json_encode($ok->getData(), JSON_UNESCAPED_SLASHES));

        return $ok;
    }
}
