<?php

declare(strict_types=1);

namespace Tests\Feature;

use App\Exceptions\PlanLimitExceededException;
use App\Models\Company;
use App\Models\Plan;
use App\Models\Subscription;
use App\Models\User;
use App\Services\PlanLimitService;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use PHPUnit\Framework\Attributes\Test;
use Spatie\Permission\Models\Role;
use Tests\TestCase;

/**
 * Verifica que los topes del plan se leen EN VIVO de la BD (ahora son editables):
 * cambiar el valor del plan cambia el cupo efectivo, `custom_limits` de la
 * suscripción lo sobre-escribe, y 0 = ilimitado. Complementa PlanLimitServiceTest
 * (que fija el bloqueo por recurso) probando la "variabilidad" del valor.
 */
class PlanLimitDbDrivenTest extends TestCase
{
    use DatabaseTransactions;

    private PlanLimitService $service;

    protected function setUp(): void
    {
        parent::setUp();
        $this->service = app(PlanLimitService::class);
    }

    /** @param array<string,int|float> $limits */
    private function companyOnPlan(array $limits, ?Plan &$plan = null, ?Subscription &$subscription = null): Company
    {
        $plan = Plan::factory()->create(array_merge([
            'max_users' => 0, 'max_collectors' => 0, 'max_active_credits' => 0,
            'max_clients' => 0, 'max_monthly_volume' => 0,
        ], $limits));

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

        return $company;
    }

    private function blocksUser(int $companyId): bool
    {
        try {
            // Instancia fresca → recarga suscripción/plan desde la BD (valor en vivo).
            $this->service->assertCanAddUser(Company::findOrFail($companyId));

            return false;
        } catch (PlanLimitExceededException) {
            return true;
        }
    }

    #[Test]
    public function the_effective_user_cap_follows_the_plan_value_in_the_db(): void
    {
        // Plan "Básico" = 2 usuarios; la empresa ya tiene 2 (al tope).
        $company = $this->companyOnPlan(['max_users' => 2], $plan);
        User::factory()->count(2)->create(['company_id' => $company->id]);

        $this->assertTrue($this->blocksUser($company->id), 'Con max_users=2 y 2 usuarios, el 3.º se bloquea');

        // Se EDITA el plan en la BD (como haría el super_admin en /admin): sube a 3.
        $plan->update(['max_users' => 3]);

        $this->assertFalse($this->blocksUser($company->id), 'Tras subir max_users a 3 en la BD, el 3.º ya se permite');

        // Se baja a 1 → ahora incluso los 2 actuales exceden → bloquea.
        $plan->update(['max_users' => 1]);

        $this->assertTrue($this->blocksUser($company->id), 'Tras bajar max_users a 1, se bloquea de inmediato');
    }

    #[Test]
    public function custom_limits_on_the_subscription_override_the_plan(): void
    {
        // Plan tope 2, pero la suscripción tiene un override de 5.
        $company = $this->companyOnPlan(['max_users' => 2], $plan, $subscription);
        User::factory()->count(3)->create(['company_id' => $company->id]); // 3 > 2 del plan

        // Sin override bloquearía (3 ≥ 2). Con override de 5, permite.
        $subscription->update(['custom_limits' => ['max_users' => 5]]);
        $this->assertFalse($this->blocksUser($company->id), 'custom_limits=5 sobre-escribe el max_users=2 del plan');

        // Quitando el override, vuelve a mandar el plan (2) → bloquea con 3 usuarios.
        $subscription->update(['custom_limits' => null]);
        $this->assertTrue($this->blocksUser($company->id), 'sin override, manda el plan (2) → bloquea');
    }

    #[Test]
    public function zero_means_unlimited(): void
    {
        $company = $this->companyOnPlan(['max_users' => 0]); // 0 = ilimitado
        User::factory()->count(25)->create(['company_id' => $company->id]);

        $this->assertFalse($this->blocksUser($company->id), 'max_users=0 → ilimitado, nunca bloquea');
    }

    #[Test]
    public function collector_cap_is_independent_from_the_user_cap(): void
    {
        Role::firstOrCreate(['name' => 'collector', 'guard_name' => 'web']);
        // Muchos usuarios permitidos, pero solo 1 cobrador.
        $company = $this->companyOnPlan(['max_users' => 0, 'max_collectors' => 1]);
        User::factory()->create(['company_id' => $company->id])->assignRole('collector'); // 1/1 cobradores

        $this->expectException(PlanLimitExceededException::class);
        $this->service->assertCanAddCollector(Company::findOrFail($company->id));
    }
}
