<?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 PHPUnit\Framework\Attributes\Test;
use Tests\TestCase;

/**
 * #164 — El acceso durante el período de gracia debe computarse EN VIVO
 * (ends_at + GRACE_PERIOD_DAYS), sin depender de que el cron nocturno
 * materialice `grace_period_ends_at`. Antes, un trial/active recién vencido
 * quedaba sin acceso hasta las 00:00 pese a tener gracia vigente.
 */
class SubscriptionGraceWindowTest extends TestCase
{
    use DatabaseTransactions;

    private Company $company;

    private Plan $plan;

    protected function setUp(): void
    {
        parent::setUp();
        $this->plan = Plan::factory()->create();
        $this->company = Company::factory()->create();
    }

    private function sub(array $attrs): void
    {
        Subscription::factory()->create(array_merge([
            'company_id' => $this->company->id,
            'plan_id' => $this->plan->id,
        ], $attrs));
    }

    private function gateAllows(): bool
    {
        Cache::forget(User::subscriptionCacheKey($this->company->id));
        $user = User::factory()->create(['company_id' => $this->company->id]);

        return $user->hasActiveSubscription();
    }

    #[Test]
    public function trial_recently_expired_within_grace_still_has_access_before_the_cron_runs(): void
    {
        // Vencido hace 2 días; el cron aún no corrió → grace_period_ends_at NULL,
        // status sigue 'trial'. Con la gracia en vivo, debe conceder acceso.
        $this->sub([
            'status' => Subscription::STATUS_TRIAL,
            'ends_at' => now()->subDays(2),
            'grace_period_ends_at' => null,
        ]);

        $this->assertTrue($this->gateAllows(), 'Dentro de la ventana de gracia debe conceder acceso.');
    }

    #[Test]
    public function trial_expired_beyond_grace_has_no_access(): void
    {
        // Vencido hace 10 días (> GRACE_PERIOD_DAYS), grace_period_ends_at NULL.
        $this->sub([
            'status' => Subscription::STATUS_TRIAL,
            'ends_at' => now()->subDays(10),
            'grace_period_ends_at' => null,
        ]);

        $this->assertFalse($this->gateAllows(), 'Pasada la gracia, sin acceso.');
    }

    #[Test]
    public function active_within_term_has_access(): void
    {
        $this->sub([
            'status' => Subscription::STATUS_ACTIVE,
            'ends_at' => now()->addMonth(),
            'grace_period_ends_at' => null,
        ]);

        $this->assertTrue($this->gateAllows());
    }

    #[Test]
    public function grace_status_after_cron_still_has_access(): void
    {
        // Estado tras el cron: status=grace, grace_period_ends_at futuro.
        $this->sub([
            'status' => Subscription::STATUS_GRACE,
            'ends_at' => now()->subDays(3),
            'grace_period_ends_at' => now()->addDays(4),
        ]);

        $this->assertTrue($this->gateAllows());
    }

    #[Test]
    public function expired_status_has_no_access_even_if_ends_at_is_recent(): void
    {
        // Regresión: una suscripción ya expirada NO debe colarse por la condición
        // en vivo (scopeActive excluye 'expired').
        $this->sub([
            'status' => Subscription::STATUS_EXPIRED,
            'ends_at' => now()->subDays(2),
            'grace_period_ends_at' => null,
            'is_active' => false,
        ]);

        $this->assertFalse($this->gateAllows());
    }
}
