<?php

declare(strict_types=1);

namespace Tests\Feature\Pwa;

use App\Models\Client;
use App\Models\Company;
use App\Models\Credit;
use App\Models\Installment;
use App\Models\Plan;
use App\Models\Subscription;
use App\Models\User;
use App\Services\PaymentManager;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Event;
use Laravel\Sanctum\Sanctum;
use Spatie\Permission\Models\Role;
use Tests\TestCase;

/**
 * Tests de seguridad: protección IDOR en PaymentController::receipt()
 *
 * Verifica que un collector solo pueda acceder al recibo de pagos
 * que él mismo registró — no los de otros collectors de la misma empresa.
 */
class PaymentReceiptAccessTest extends TestCase
{
    use DatabaseTransactions;

    private Company $company;

    private Plan $plan;

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

        Event::fake(); // Evitar listeners asíncronos que puedan interferir

        // Crear roles necesarios para pwa.access middleware
        foreach (['admin', 'supervisor', 'collector'] as $role) {
            Role::firstOrCreate(['name' => $role, 'guard_name' => 'web']);
        }

        // Empresa con plan y suscripción activa (requeridos por EnsurePwaAccess)
        $this->plan = Plan::factory()->create(['has_pwa_access' => true]);
        $this->company = Company::factory()->create();

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

    /**
     * Helper: crea un collector en la empresa de prueba.
     */
    private function makeCollector(string $name = 'Collector'): User
    {
        $user = User::factory()->create([
            'company_id' => $this->company->id,
            'name' => $name,
        ]);
        $user->assignRole('collector');

        return $user->fresh();
    }

    /**
     * Helper: crea un crédito activo con una cuota pendiente.
     * El crédito queda asignado al $collector recibido.
     */
    private function makeCredit(User $collector): Credit
    {
        $client = Client::factory()->create(['company_id' => $this->company->id]);

        $credit = Credit::factory()->create([
            'company_id' => $this->company->id,
            'client_id' => $client->id,
            'collector_user_id' => $collector->id,
            'created_by_user_id' => $collector->id,
            'status' => Credit::STATUS_ACTIVE,
            'amount' => 100_000,
            'installments_count' => 1,
            'periodicity' => 'monthly',
            'start_date' => now()->subDays(5),
            'due_date' => now()->addDays(25),
        ]);

        Installment::factory()->create([
            'company_id' => $this->company->id,
            'credit_id' => $credit->id,
            'installment_number' => 1,
            'status' => Installment::STATUS_PENDING,
            'amount_paid' => 0,
            'total_amount' => 100_000,
            'principal_amount' => 80_000,
            'interest_amount' => 20_000,
            'principal_balance_after' => 100_000,
            'due_date' => now()->addDays(25),
        ]);

        return $credit;
    }

    /**
     * IDOR: Un collector NO debe poder ver el recibo de un pago
     * registrado por otro collector, aunque sean de la misma empresa.
     */
    public function test_collector_cannot_see_other_collector_receipt(): void
    {
        $collector1 = $this->makeCollector('Collector 1');
        $collector2 = $this->makeCollector('Collector 2');

        // El crédito está asignado a collector2
        $credit = $this->makeCredit($collector2);

        // Autenticar como collector2 para registrar el pago usando PaymentManager
        // (PaymentManager requiere auth activo para MultiTenantScope)
        Sanctum::actingAs($collector2);

        $payment = app(PaymentManager::class)->registerPayment(
            amount: 50_000.0,
            paymentDate: Carbon::today(),
            registeredByUserId: $collector2->id,
            credit: $credit,
            paymentMethod: 'cash',
        );

        // Ahora autenticar como collector1 (diferente cobrador, misma empresa)
        Sanctum::actingAs($collector1);

        $response = $this->getJson("/api/pwa/payments/{$payment->id}/receipt");

        // Collector1 no registró este pago — debe recibir 404
        $response->assertStatus(404);
    }

    /**
     * Un collector SÍ puede ver el recibo de un pago que él mismo registró.
     */
    public function test_collector_can_see_own_receipt(): void
    {
        $collector1 = $this->makeCollector('Collector 1');

        // El crédito está asignado a collector1
        $credit = $this->makeCredit($collector1);

        // Autenticar como collector1 y registrar el pago
        Sanctum::actingAs($collector1);

        $payment = app(PaymentManager::class)->registerPayment(
            amount: 50_000.0,
            paymentDate: Carbon::today(),
            registeredByUserId: $collector1->id,
            credit: $credit,
            paymentMethod: 'cash',
        );

        // Collector1 consulta el recibo de su propio pago
        $response = $this->getJson("/api/pwa/payments/{$payment->id}/receipt");

        $response->assertStatus(200)
            ->assertJsonStructure(['receipt']);
    }
}
