<?php

declare(strict_types=1);

namespace Tests\Feature\Security;

use App\Models\Company;
use App\Models\Plan;
use App\Models\Subscription;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Str;
use PHPUnit\Framework\Attributes\Test;
use Spatie\Permission\Models\Role;
use Tests\TestCase;

/**
 * FIX H-01: Supervisor bloqueado en POST /api/pwa/sync/payments.
 *
 * canRegisterPayments() solo permite collector y admin.
 * El guard se aplica al inicio de SyncController::syncPayments(),
 * DESPUÉS de que SyncPaymentsRequest valida la estructura del payload.
 * Por ello los tests usan payloads estructuralmente válidos para llegar al guard.
 */
class SyncPaymentRoleGuardTest extends TestCase
{
    use DatabaseTransactions;

    private const SYNC_URL = '/api/pwa/sync/payments';

    private Company $company;

    private User $collector;

    private User $supervisor;

    private User $admin;

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

        foreach (['admin', 'supervisor', 'collector'] as $role) {
            Role::firstOrCreate(['name' => $role, 'guard_name' => 'web']);
        }

        $plan = Plan::factory()->create();
        $this->company = Company::factory()->create();

        Subscription::factory()->create([
            'company_id' => $this->company->id,
            'plan_id' => $plan->id,
            'status' => Subscription::STATUS_ACTIVE,
            'is_active' => true,
            'starts_at' => now()->subMonth(),
            'ends_at' => now()->addYear(),
        ]);

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

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

        $this->admin = User::factory()->create(['company_id' => $this->company->id]);
        $this->admin->assignRole('admin');
        $this->admin = $this->admin->fresh();
    }

    private function tokenFor(User $user): string
    {
        $role = $user->getRoleNames()->first();

        return $user->createToken('device', ["pwa:{$role}"])->plainTextToken;
    }

    /**
     * Payload estructuralmente válido — pasa SyncPaymentsRequest validation para
     * que el controller guard sea alcanzado. El credit_id=999 no existirá, pero
     * eso se evalúa dentro del procesamiento, no en la validación de estructura.
     */
    private function validFormatPayload(): array
    {
        return [
            'payments' => [
                [
                    'credit_id' => 999,
                    'amount' => 100,
                    'payment_date' => now()->toDateString(),
                    'idempotency_key' => (string) Str::uuid(),
                ],
            ],
        ];
    }

    #[Test]
    public function supervisor_receives_403_on_sync_payments(): void
    {
        $token = $this->tokenFor($this->supervisor);

        $this->withToken($token)
            ->postJson(self::SYNC_URL, $this->validFormatPayload())
            ->assertStatus(403)
            ->assertJson(['message' => 'No tienes permiso para registrar pagos.']);
    }

    #[Test]
    public function collector_reaches_processing_logic_not_403(): void
    {
        $token = $this->tokenFor($this->collector);

        // Guard pasa → pago procesado (aunque falle por crédito inexistente).
        // Importante: la respuesta NO es 403.
        $response = $this->withToken($token)
            ->postJson(self::SYNC_URL, $this->validFormatPayload());

        $response->assertStatus(200); // 200 con results/summary del batch
        $response->assertJsonMissing(['message' => 'No tienes permiso para registrar pagos.']);
    }

    #[Test]
    public function admin_reaches_processing_logic_not_403(): void
    {
        $token = $this->tokenFor($this->admin);

        $response = $this->withToken($token)
            ->postJson(self::SYNC_URL, $this->validFormatPayload());

        $response->assertStatus(200);
        $response->assertJsonMissing(['message' => 'No tienes permiso para registrar pagos.']);
    }
}
