<?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\SupervisorCollector;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Laravel\Sanctum\Sanctum;
use PHPUnit\Framework\Attributes\Test;
use Spatie\Permission\Models\Role;
use Tests\TestCase;

/**
 * Visibilidad supervisor→cobrador (#3 / #71) en CollectionController:
 * un supervisor asignado solo ve las cuotas de SU equipo, no de toda la empresa,
 * y no puede espiar a otro cobrador pasando ?collector_id.
 */
class SupervisorCollectionVisibilityTest extends TestCase
{
    use DatabaseTransactions;

    private Company $company;

    private User $supervisor;

    private User $collectorInTeam;

    private User $collectorOutside;

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

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

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

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

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

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

        SupervisorCollector::create([
            'company_id' => $this->company->id,
            'supervisor_id' => $this->supervisor->id,
            'collector_id' => $this->collectorInTeam->id,
        ]);
    }

    /**
     * Crea un crédito activo con una cuota pendiente asignado al cobrador dado.
     */
    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;
    }

    /** @return int[] credit_ids presentes en la respuesta del index */
    private function indexCreditIds(array $query = []): array
    {
        $response = $this->getJson('/api/pwa/collections?'.http_build_query(array_merge(['filter' => 'all'], $query)));
        $response->assertStatus(200);

        return collect($response->json('data'))->pluck('credit_id')->all();
    }

    #[Test]
    public function supervisor_does_not_see_collections_outside_their_team(): void
    {
        $teamCredit = $this->makeCredit($this->collectorInTeam);
        $outsideCredit = $this->makeCredit($this->collectorOutside);

        Sanctum::actingAs($this->supervisor);
        $creditIds = $this->indexCreditIds();

        $this->assertContains($teamCredit->id, $creditIds, 'Debe ver las cuotas de su equipo');
        $this->assertNotContains($outsideCredit->id, $creditIds, 'NO debe ver cuotas de cobradores fuera de su equipo');
    }

    #[Test]
    public function supervisor_cannot_spy_outside_collector_via_collector_id(): void
    {
        $outsideCredit = $this->makeCredit($this->collectorOutside);

        Sanctum::actingAs($this->supervisor);
        // Pasa el collector_id de un cobrador fuera de su equipo → no debe ver nada.
        $creditIds = $this->indexCreditIds(['collector_id' => $this->collectorOutside->id]);

        $this->assertNotContains($outsideCredit->id, $creditIds);
        $this->assertEmpty($creditIds, 'Filtrar por un cobrador fuera del equipo no debe devolver datos');
    }

    #[Test]
    public function supervisor_stats_only_count_their_team(): void
    {
        // Solo existe cartera del cobrador externo → el supervisor scoped ve 0.
        $this->makeCredit($this->collectorOutside);

        Sanctum::actingAs($this->supervisor);
        $response = $this->getJson('/api/pwa/collections/stats');

        $response->assertStatus(200)
            ->assertJsonPath('all.count', 0);
    }

    #[Test]
    public function sees_all_supervisor_sees_every_collector(): void
    {
        $this->supervisor->update(['sees_all_collectors' => true]);

        $teamCredit = $this->makeCredit($this->collectorInTeam);
        $outsideCredit = $this->makeCredit($this->collectorOutside);

        Sanctum::actingAs($this->supervisor->fresh());
        $creditIds = $this->indexCreditIds();

        $this->assertContains($teamCredit->id, $creditIds);
        $this->assertContains($outsideCredit->id, $creditIds, 'Con sees_all_collectors debe ver toda la empresa');
    }
}
