<?php

declare(strict_types=1);

namespace Tests\Unit;

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

/**
 * Tests unitarios para CollectorVisibilityResolver.
 *
 * Verifica los cuatro modos de visibilidad independientemente del controller:
 *   - admin → todos ('all')
 *   - supervisor + sees_all_collectors=true → todos ('all')
 *   - supervisor + asignaciones → sus collectors ('assigned')
 *   - supervisor sin asignaciones → 'unassigned'
 */
class CollectorVisibilityResolverTest extends TestCase
{
    use DatabaseTransactions;

    private CollectorVisibilityResolver $resolver;

    private Company $company;

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

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

        $this->resolver = new CollectorVisibilityResolver;

        $this->company = Company::factory()->create();
        Subscription::factory()->create([
            'company_id' => $this->company->id,
            'is_active' => true,
            'ends_at' => now()->addYear(),
        ]);
    }

    // ------------------------------------------------------------------
    // Helpers
    // ------------------------------------------------------------------

    private function makeUser(string $role, array $attrs = []): User
    {
        $user = User::factory()->create(array_merge(['company_id' => $this->company->id], $attrs));
        $user->assignRole($role);

        return $user->fresh();
    }

    // ------------------------------------------------------------------
    // Tests
    // ------------------------------------------------------------------

    #[Test]
    public function admin_always_sees_all_collectors(): void
    {
        $admin = $this->makeUser('admin');

        $result = $this->resolver->resolve($admin);

        $this->assertSame(CollectorVisibilityResolver::MODE_ALL, $result['mode']);
        $this->assertEmpty($result['collector_ids']);
    }

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

        $result = $this->resolver->resolve($supervisor);

        $this->assertSame(CollectorVisibilityResolver::MODE_ALL, $result['mode']);
        $this->assertEmpty($result['collector_ids']);
    }

    #[Test]
    public function supervisor_with_assigned_collectors_sees_only_assigned(): void
    {
        $supervisor = $this->makeUser('supervisor');
        $collector1 = $this->makeUser('collector');
        $collector2 = $this->makeUser('collector');
        $other = $this->makeUser('collector');  // no asignado

        $supervisor->assignedCollectors()->attach($collector1->id);
        $supervisor->assignedCollectors()->attach($collector2->id);

        $result = $this->resolver->resolve($supervisor->fresh());

        $this->assertSame(CollectorVisibilityResolver::MODE_ASSIGNED, $result['mode']);
        $this->assertCount(2, $result['collector_ids']);
        $this->assertContains($collector1->id, $result['collector_ids']);
        $this->assertContains($collector2->id, $result['collector_ids']);
        $this->assertNotContains($other->id, $result['collector_ids']);
    }

    #[Test]
    public function supervisor_without_assignments_is_unassigned(): void
    {
        $supervisor = $this->makeUser('supervisor');

        $result = $this->resolver->resolve($supervisor);

        $this->assertSame(CollectorVisibilityResolver::MODE_UNASSIGNED, $result['mode']);
        $this->assertEmpty($result['collector_ids']);
    }

    #[Test]
    public function sees_all_flag_overrides_empty_assignment_list(): void
    {
        // Un supervisor sin asignaciones pero con sees_all=true → debe ver todo, no 'unassigned'.
        $supervisor = $this->makeUser('supervisor', ['sees_all_collectors' => true]);

        $result = $this->resolver->resolve($supervisor);

        $this->assertSame(CollectorVisibilityResolver::MODE_ALL, $result['mode']);
    }

    #[Test]
    public function apply_to_query_returns_null_when_unassigned(): void
    {
        $supervisor = $this->makeUser('supervisor');

        $query = User::role('collector')->where('users.company_id', $this->company->id);
        $result = $this->resolver->applyToQuery($query, $supervisor);

        $this->assertNull($result);
    }

    #[Test]
    public function apply_to_query_filters_by_assigned_ids(): void
    {
        $supervisor = $this->makeUser('supervisor');
        $assigned = $this->makeUser('collector');
        $notAssigned = $this->makeUser('collector');

        $supervisor->assignedCollectors()->attach($assigned->id);

        $query = User::role('collector')->where('users.company_id', $this->company->id);
        $filtered = $this->resolver->applyToQuery($query, $supervisor->fresh());

        $ids = $filtered->pluck('users.id')->all();
        $this->assertContains($assigned->id, $ids);
        $this->assertNotContains($notAssigned->id, $ids);
    }

    #[Test]
    public function apply_to_query_returns_full_query_for_admin(): void
    {
        $admin = $this->makeUser('admin');
        $collector1 = $this->makeUser('collector');
        $collector2 = $this->makeUser('collector');

        $query = User::role('collector')->where('users.company_id', $this->company->id);
        $filtered = $this->resolver->applyToQuery($query, $admin);

        $this->assertNotNull($filtered);
        $ids = $filtered->pluck('users.id')->all();
        $this->assertContains($collector1->id, $ids);
        $this->assertContains($collector2->id, $ids);
    }

    // ------------------------------------------------------------------
    // visibleCollectorIds(): null = todos, [] = ninguno, int[] = equipo
    // ------------------------------------------------------------------

    #[Test]
    public function visible_collector_ids_is_null_for_admin(): void
    {
        $admin = $this->makeUser('admin');

        $this->assertNull($this->resolver->visibleCollectorIds($admin));
    }

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

        $this->assertNull($this->resolver->visibleCollectorIds($supervisor));
    }

    #[Test]
    public function visible_collector_ids_returns_assigned_ids(): void
    {
        $supervisor = $this->makeUser('supervisor');
        $assigned = $this->makeUser('collector');
        $other = $this->makeUser('collector');

        $supervisor->assignedCollectors()->attach($assigned->id);

        $ids = $this->resolver->visibleCollectorIds($supervisor->fresh());

        $this->assertSame([$assigned->id], $ids);
        $this->assertNotContains($other->id, $ids);
    }

    #[Test]
    public function visible_collector_ids_is_empty_array_for_unassigned_supervisor(): void
    {
        $supervisor = $this->makeUser('supervisor');

        // [] (no null) → la query con whereIn('collector_user_id', []) no trae nada.
        $this->assertSame([], $this->resolver->visibleCollectorIds($supervisor));
    }
}
