<?php

declare(strict_types=1);

namespace App\Services\Sync;

use App\Http\Middleware\EnsurePwaAccess;
use App\Models\User;
use Illuminate\Database\Eloquent\Builder;

/**
 * Quién capturó un ítem de la cola offline.
 *
 * El token dice quién SUBE, no quién COBRÓ: en un teléfono compartido la cola
 * de quien lo usó antes se sube con la sesión de quien entra después. Solo se
 * puede atribuir a un usuario de la MISMA empresa que además tenga un rol de
 * la PWA (collector/supervisor/admin) — ver `EnsurePwaAccess::ALLOWED_ROLES`.
 */
class CapturerResolver
{
    /**
     * Memoiza por "company_id:id" dentro de esta instancia: un lote de la cola
     * puede traer decenas de ítems del mismo capturador foráneo y no hace
     * falta repetir la consulta. Se guarda también el `null` de "no
     * encontrado". La empresa forma parte de la key para que una instancia
     * reusada entre syncers de distinta empresa nunca devuelva al usuario de
     * otra.
     *
     * @var array<string, User|null>
     */
    private array $cache = [];

    /**
     * - Sin `captured_by_user_id` (ítems anteriores a este campo) o igual al que
     *   sube → el mismo usuario autenticado, como hasta ahora.
     * - Otro usuario de la MISMA empresa con rol de la PWA → ese usuario.
     * - Inexistente, ilegible, sin rol de la PWA o de otra empresa → null: no
     *   se puede atribuir.
     */
    public function resolve(mixed $capturedById, User $syncer): ?User
    {
        if ($capturedById === null || $capturedById === '') {
            return $syncer;
        }

        // filter_var() trata un bool como escalar y lo castea a 0/1 antes de
        // validar (true pasaría como id 1), así que se descarta explícitamente.
        if (is_bool($capturedById)) {
            return null;
        }

        $id = filter_var($capturedById, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);

        if ($id === false) {
            return null;
        }

        if ($id === $syncer->id) {
            return $syncer;
        }

        if ($syncer->company_id === null) {
            return null;
        }

        $cacheKey = $syncer->company_id.':'.$id;

        if (array_key_exists($cacheKey, $this->cache)) {
            return $this->cache[$cacheKey];
        }

        // No se usa el scope ->role() de Spatie: internamente resuelve cada
        // nombre con Role::findByName() y LANZA RoleDoesNotExist si el rol no
        // existe todavía en esta base (p. ej. en CI, donde solo se siembran
        // los roles que cada test necesita). whereHas() sobre la relación no
        // lanza nada si no hay coincidencias, solo no encuentra filas.
        return $this->cache[$cacheKey] = User::query()
            ->without('company')
            ->where('company_id', $syncer->company_id)
            ->whereHas('roles', function (Builder $q): void {
                $q->whereIn('name', EnsurePwaAccess::ALLOWED_ROLES);
            })
            ->find($id);
    }
}
