<?php

declare(strict_types=1);

namespace Tests\Feature\Pwa;

use App\Models\Company;
use App\Models\User;
use App\Services\Sync\CapturerResolver;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use PHPUnit\Framework\Attributes\Test;
use Spatie\Permission\Models\Role;
use Tests\TestCase;

/**
 * Cada ítem de la cola offline dice quién lo capturó (`captured_by_user_id`).
 * Un teléfono compartido sube la cola de quien lo usó antes con el token de
 * quien entra ahora, así que el servidor no puede atribuir por token. Solo se
 * puede atribuir a un usuario de la MISMA empresa que además tenga un rol de
 * la PWA (collector/supervisor/admin).
 */
class CapturerResolverTest extends TestCase
{
    use DatabaseTransactions;

    private function ensureRole(string $name): void
    {
        Role::firstOrCreate(['name' => $name, 'guard_name' => 'web']);
    }

    #[Test]
    public function sin_capturador_es_quien_sube(): void
    {
        $company = Company::factory()->create();
        $syncer = User::factory()->create(['company_id' => $company->id]);

        $this->assertSame($syncer->id, app(CapturerResolver::class)->resolve(null, $syncer)?->id);
    }

    #[Test]
    public function id_vacio_o_igual_al_que_sube_devuelve_el_mismo_usuario_sin_consultar(): void
    {
        $company = Company::factory()->create();
        $syncer = User::factory()->create(['company_id' => $company->id]);
        $resolver = app(CapturerResolver::class);

        $this->assertSame($syncer, $resolver->resolve('', $syncer));
        $this->assertSame($syncer, $resolver->resolve($syncer->id, $syncer));
    }

    #[Test]
    public function un_id_numerico_como_string_resuelve_al_capturador(): void
    {
        $this->ensureRole('collector');

        $company = Company::factory()->create();
        $syncer = User::factory()->create(['company_id' => $company->id]);
        $capturer = User::factory()->create(['company_id' => $company->id]);
        $capturer->assignRole('collector');

        $resolved = app(CapturerResolver::class)->resolve((string) $capturer->id, $syncer);

        $this->assertSame($capturer->id, $resolved?->id);
    }

    #[Test]
    public function otro_usuario_de_la_misma_empresa_con_rol_pwa_es_el_capturador(): void
    {
        $this->ensureRole('collector');

        $company = Company::factory()->create();
        $syncer = User::factory()->create(['company_id' => $company->id]);
        $capturer = User::factory()->create(['company_id' => $company->id]);
        $capturer->assignRole('collector');

        $this->assertSame($capturer->id, app(CapturerResolver::class)->resolve($capturer->id, $syncer)?->id);
    }

    #[Test]
    public function un_usuario_de_la_misma_empresa_sin_rol_pwa_no_se_puede_atribuir(): void
    {
        $company = Company::factory()->create();
        $syncer = User::factory()->create(['company_id' => $company->id]);
        $capturerSinRol = User::factory()->create(['company_id' => $company->id]);

        $this->assertNull(app(CapturerResolver::class)->resolve($capturerSinRol->id, $syncer));
    }

    #[Test]
    public function un_usuario_de_otra_empresa_o_inexistente_no_se_puede_atribuir(): void
    {
        $this->ensureRole('collector');

        $company = Company::factory()->create();
        $syncer = User::factory()->create(['company_id' => $company->id]);
        $foreigner = User::factory()->create(['company_id' => Company::factory()->create()->id]);
        $foreigner->assignRole('collector');

        $resolver = app(CapturerResolver::class);

        $this->assertNull($resolver->resolve($foreigner->id, $syncer));
        $this->assertNull($resolver->resolve(999_999_999, $syncer));
        $this->assertNull($resolver->resolve('no-es-un-id', $syncer));
    }

    #[Test]
    public function ids_mal_formados_no_se_pueden_atribuir(): void
    {
        $this->ensureRole('collector');

        $company = Company::factory()->create();
        $syncer = User::factory()->create(['company_id' => $company->id]);
        $capturer = User::factory()->create(['company_id' => $company->id]);
        $capturer->assignRole('collector');
        $resolver = app(CapturerResolver::class);

        $this->assertNull($resolver->resolve(1.5, $syncer));
        $this->assertNull($resolver->resolve('0', $syncer));
        $this->assertNull($resolver->resolve(-3, $syncer));
        $this->assertNull($resolver->resolve(true, $syncer));
        $this->assertNull($resolver->resolve('1e2', $syncer));

        // Un (int) cast habría truncado estos dos a $capturer->id y lo habría
        // resuelto igual; filter_var() con FILTER_VALIDATE_INT los rechaza.
        $this->assertNull($resolver->resolve($capturer->id + 0.5, $syncer));
        $this->assertNull($resolver->resolve($capturer->id.'.9', $syncer));
    }

    #[Test]
    public function un_syncer_sin_empresa_no_puede_atribuir_a_otro_usuario(): void
    {
        $this->ensureRole('collector');

        // El capturador también tiene company_id null: sin el guard explícito
        // de "syncer sin empresa", where('company_id', null) degradaría a
        // whereNull('company_id') y encontraría a este mismo usuario.
        $syncer = User::factory()->create(['company_id' => null]);
        $capturer = User::factory()->create(['company_id' => null]);
        $capturer->assignRole('collector');

        $this->assertNull(app(CapturerResolver::class)->resolve($capturer->id, $syncer));
    }

    #[Test]
    public function no_falla_si_faltan_roles_en_la_base(): void
    {
        // Simula CI: la base solo tiene sembrado el rol que este test necesita
        // ('collector'). El scope ->role() de Spatie llama a Role::findByName()
        // por cada nombre pedido y LANZA RoleDoesNotExist si alguno no existe
        // todavía; whereHas() sobre la relación 'roles' no debe lanzar nada
        // aunque 'supervisor'/'admin' no existan en esta base.
        Role::whereIn('name', ['supervisor', 'admin'])->delete();
        $this->ensureRole('collector');

        $company = Company::factory()->create();
        $syncer = User::factory()->create(['company_id' => $company->id]);
        $capturer = User::factory()->create(['company_id' => $company->id]);
        $capturer->assignRole('collector');

        $resolved = app(CapturerResolver::class)->resolve($capturer->id, $syncer);

        $this->assertSame($capturer->id, $resolved?->id);
    }
}
