<?php

declare(strict_types=1);

namespace Tests\Feature;

use App\Models\Company;
use App\Models\Plan;
use App\Models\Subscription;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Spatie\Permission\Models\Role;
use Tests\TestCase;

/**
 * Verifica que User::hasActiveSubscription() cachea el resultado por company_id
 * (lo consulta EnsurePwaAccess en cada request PWA) y que la cache se invalida
 * cuando una Subscription cambia, de modo que una renovación se refleje al instante.
 */
class SubscriptionStatusCacheTest extends TestCase
{
    use DatabaseTransactions;

    private Company $company;

    private User $user;

    protected function setUp(): void
    {
        parent::setUp();

        Role::firstOrCreate(['name' => 'collector', 'guard_name' => 'web']);

        $plan = Plan::factory()->create(['has_pwa_access' => true]);
        $this->company = Company::factory()->create();

        Subscription::factory()->active()->create([
            'company_id' => $this->company->id,
            'plan_id' => $plan->id,
            'ends_at' => now()->addYear(),
        ]);

        $this->user = User::factory()->create(['company_id' => $this->company->id]);
        $this->user->assignRole('collector');

        Cache::forget(User::subscriptionCacheKey($this->company->id));
    }

    public function test_active_subscription_returns_true_and_is_cached(): void
    {
        $this->assertTrue($this->user->hasActiveSubscription());

        // La segunda llamada no debe ejecutar ninguna query contra `subscriptions`.
        DB::enableQueryLog();
        $this->assertTrue($this->user->hasActiveSubscription());
        $queries = DB::getQueryLog();
        DB::disableQueryLog();

        $touchedSubscriptions = collect($queries)
            ->contains(fn (array $q): bool => str_contains($q['query'], 'subscriptions'));

        $this->assertFalse(
            $touchedSubscriptions,
            'La segunda llamada debería leer de cache, sin tocar la tabla subscriptions.'
        );
    }

    public function test_saving_a_subscription_invalidates_the_cache(): void
    {
        // Cachea el estado actual (true).
        $this->assertTrue($this->user->hasActiveSubscription());
        $this->assertNotNull(Cache::get(User::subscriptionCacheKey($this->company->id)));

        // Expirar todas las suscripciones de la company invalida la cache vía Subscription::booted.
        $this->company->subscriptions()->get()->each->expire();

        $this->assertNull(
            Cache::get(User::subscriptionCacheKey($this->company->id)),
            'Guardar una Subscription debe limpiar la cache de estado de la company.'
        );

        // Una nueva consulta refleja el estado actualizado sin esperar al TTL.
        $this->assertFalse($this->user->fresh()->hasActiveSubscription());
    }
}
