<?php

declare(strict_types=1);

namespace Tests\Feature\Security;

use App\Models\Company;
use App\Models\Subscription;
use App\Models\SubscriptionRequest;
use App\Models\User;
use Filament\Facades\Filament;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Gate;
use PHPUnit\Framework\Attributes\Test;
use Spatie\Permission\Models\Role;
use Tests\TestCase;

/**
 * #124 — Autorización del panel Filament: cierra el "default-allow".
 * - canAccessPanel: solo super_admin/admin (rol); no supervisor/collector.
 * - El gate de SUSCRIPCIÓN vive en el middleware EnsureActiveSubscription (Fase 3),
 *   que redirige a /suscripcion en vez del 403 (la prioridad de middleware de Laravel
 *   impide gatearlo en canAccessPanel). La garantía de acceso es la misma.
 * - Subscription/SubscriptionRequest/Company: solo super_admin (todas las acciones).
 * - User: un admin gestiona SOLO su propia empresa; super_admin todo; otros roles nada.
 */
class FilamentPanelAuthorizationTest extends TestCase
{
    use DatabaseTransactions;

    protected function setUp(): void
    {
        parent::setUp();
        Cache::flush(); // hasActiveSubscription cachea por empresa
        foreach (['super_admin', 'admin', 'supervisor', 'collector', 'client'] as $r) {
            Role::firstOrCreate(['name' => $r, 'guard_name' => 'web']);
        }
    }

    private function companyWithActiveSubscription(): Company
    {
        $company = Company::factory()->create();
        Subscription::factory()->active()->create([
            'company_id' => $company->id,
            'ends_at' => now()->addYear(),
        ]);

        return $company;
    }

    private function user(string $role, Company $company): User
    {
        $u = User::factory()->create(['company_id' => $company->id]);
        $u->assignRole($role);

        return $u->fresh();
    }

    private function superAdmin(): User
    {
        $u = User::factory()->create(['company_id' => null]);
        $u->assignRole('super_admin');

        return $u->fresh();
    }

    #[Test]
    public function panel_access_is_limited_to_super_admin_and_company_admin(): void
    {
        $panel = Filament::getDefaultPanel();
        $company = $this->companyWithActiveSubscription(); // todos con suscripción activa → aísla el gate de ROL

        $this->assertTrue($this->superAdmin()->canAccessPanel($panel), 'super_admin debe acceder');
        $this->assertTrue($this->user('admin', $company)->canAccessPanel($panel), 'admin debe acceder');
        $this->assertFalse($this->user('supervisor', $company)->canAccessPanel($panel), 'supervisor NO debe acceder');
        $this->assertFalse($this->user('collector', $company)->canAccessPanel($panel), 'collector NO debe acceder');
    }

    #[Test]
    public function panel_access_requires_active_subscription_even_for_admin(): void
    {
        // El gate de suscripción se movió de canAccessPanel al middleware
        // EnsureActiveSubscription (ver User::canAccessPanel). La garantía es la misma:
        // un admin sin suscripción vigente NO puede usar el panel — ahora se le redirige
        // a la pantalla de reactivación en vez de recibir un 403.
        $company = Company::factory()->create(); // SIN suscripción
        $admin = $this->user('admin', $company);

        $this->actingAs($admin)
            ->get('/admin')
            ->assertRedirect(route('subscription.locked'));
    }

    #[Test]
    public function platform_resources_are_super_admin_only(): void
    {
        $company = $this->companyWithActiveSubscription();
        $admin = $this->user('admin', $company);
        $super = $this->superAdmin();

        foreach ([Subscription::class, SubscriptionRequest::class, Company::class] as $model) {
            $this->assertFalse(Gate::forUser($admin)->allows('viewAny', $model), "admin NO viewAny {$model}");
            $this->assertFalse(Gate::forUser($admin)->allows('create', $model), "admin NO create {$model}");
            $this->assertTrue(Gate::forUser($super)->allows('viewAny', $model), "super viewAny {$model}");
            $this->assertTrue(Gate::forUser($super)->allows('create', $model), "super create {$model}");
        }
    }

    #[Test]
    public function company_admin_cannot_view_another_companys_subscription_request(): void
    {
        $companyA = $this->companyWithActiveSubscription();
        $adminA = $this->user('admin', $companyA);

        $foreignRequest = new SubscriptionRequest; // la policy super-only deniega a cualquier no-super

        $this->assertFalse(Gate::forUser($adminA)->allows('view', $foreignRequest), 'IDOR cerrado');
        $this->assertTrue(Gate::forUser($this->superAdmin())->allows('view', $foreignRequest));
    }

    #[Test]
    public function user_management_is_scoped_to_own_company_for_admins(): void
    {
        $companyA = $this->companyWithActiveSubscription();
        $companyB = $this->companyWithActiveSubscription();
        $adminA = $this->user('admin', $companyA);

        $ownUser = User::factory()->make(['company_id' => $companyA->id]);
        $foreignUser = User::factory()->make(['company_id' => $companyB->id]);

        $this->assertTrue(Gate::forUser($adminA)->allows('viewAny', User::class));
        $this->assertTrue(Gate::forUser($adminA)->allows('create', User::class));
        $this->assertTrue(Gate::forUser($adminA)->allows('update', $ownUser), 'admin edita usuario de SU empresa');
        $this->assertFalse(Gate::forUser($adminA)->allows('update', $foreignUser), 'admin NO edita usuario de otra empresa');
    }

    #[Test]
    public function supervisor_cannot_manage_users(): void
    {
        $company = $this->companyWithActiveSubscription();
        $supervisor = $this->user('supervisor', $company);
        $target = User::factory()->make(['company_id' => $company->id]);

        $this->assertFalse(Gate::forUser($supervisor)->allows('viewAny', User::class));
        $this->assertFalse(Gate::forUser($supervisor)->allows('create', User::class), 'cierra escalación supervisor→admin');
        $this->assertFalse(Gate::forUser($supervisor)->allows('update', $target));
    }

    #[Test]
    public function admin_cannot_delete_self_but_can_delete_own_company_users(): void
    {
        $company = $this->companyWithActiveSubscription();
        $admin = $this->user('admin', $company);
        $other = User::factory()->create(['company_id' => $company->id]);

        $this->assertFalse(Gate::forUser($admin)->allows('delete', $admin), 'admin no se elimina a sí mismo');
        $this->assertTrue(Gate::forUser($admin)->allows('delete', $other), 'admin elimina a otro de su empresa');
    }
}
