<?php

declare(strict_types=1);

namespace Tests\Feature;

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

/**
 * Tests de la API de gestión de asignaciones supervisor ↔ collector.
 *
 * Cubre:
 *   - Endpoints GET/POST/DELETE/PATCH bajo /api/pwa/team/*
 *   - Autorización: solo admin puede gestionar asignaciones
 *   - Multi-tenancy: no se puede asignar collector de otra empresa
 *   - Integridad: la tabla supervisor_collector recibe company_id correcto
 */
class SupervisorTeamAssignmentTest extends TestCase
{
    use DatabaseTransactions;

    private Company $company;

    private User $admin;

    private User $supervisor;

    private User $collector;

    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->admin = $this->makeUser('admin');
        $this->supervisor = $this->makeUser('supervisor');
        $this->collector = $this->makeUser('collector');
    }

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

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

        return $user->fresh();
    }

    private function actingAsAdmin(): static
    {
        return $this->actingAs($this->admin, 'sanctum');
    }

    // ------------------------------------------------------------------
    // GET /api/pwa/team/collectors
    // ------------------------------------------------------------------

    #[Test]
    public function admin_can_list_company_collectors(): void
    {
        $this->actingAsAdmin()
            ->getJson('/api/pwa/team/collectors')
            ->assertOk()
            ->assertJsonPath('data.0.id', $this->collector->id);
    }

    #[Test]
    public function supervisor_cannot_list_collectors(): void
    {
        $this->actingAs($this->supervisor, 'sanctum')
            ->getJson('/api/pwa/team/collectors')
            ->assertForbidden();
    }

    // ------------------------------------------------------------------
    // POST /api/pwa/team/supervisors/{id}/collectors
    // ------------------------------------------------------------------

    #[Test]
    public function admin_can_assign_collector_to_supervisor(): void
    {
        $this->actingAsAdmin()
            ->postJson("/api/pwa/team/supervisors/{$this->supervisor->id}/collectors", [
                'collector_id' => $this->collector->id,
            ])
            ->assertCreated()
            ->assertJsonFragment(['supervisor_id' => $this->supervisor->id]);

        $this->assertDatabaseHas('supervisor_collector', [
            'supervisor_id' => $this->supervisor->id,
            'collector_id' => $this->collector->id,
            'company_id' => $this->company->id,
        ]);
    }

    #[Test]
    public function assigning_same_collector_twice_returns_422(): void
    {
        $this->supervisor->assignedCollectors()->attach($this->collector->id);

        $this->actingAsAdmin()
            ->postJson("/api/pwa/team/supervisors/{$this->supervisor->id}/collectors", [
                'collector_id' => $this->collector->id,
            ])
            ->assertUnprocessable();
    }

    #[Test]
    public function cannot_assign_collector_from_another_company(): void
    {
        $otherCompany = Company::factory()->create();
        $otherCollector = $this->makeUser('collector', $otherCompany);

        $this->actingAsAdmin()
            ->postJson("/api/pwa/team/supervisors/{$this->supervisor->id}/collectors", [
                'collector_id' => $otherCollector->id,
            ])
            ->assertNotFound();

        $this->assertDatabaseMissing('supervisor_collector', [
            'collector_id' => $otherCollector->id,
        ]);
    }

    #[Test]
    public function cannot_assign_to_supervisor_from_another_company(): void
    {
        $otherCompany = Company::factory()->create();
        $otherSupervisor = $this->makeUser('supervisor', $otherCompany);

        $this->actingAsAdmin()
            ->postJson("/api/pwa/team/supervisors/{$otherSupervisor->id}/collectors", [
                'collector_id' => $this->collector->id,
            ])
            ->assertNotFound();
    }

    #[Test]
    public function pivot_company_id_is_auto_injected_from_supervisor_when_missing(): void
    {
        // Usa attach() sin company_id explícito para verificar que booted() lo inyecta.
        $this->supervisor->assignedCollectors()->attach($this->collector->id);

        $this->assertDatabaseHas('supervisor_collector', [
            'supervisor_id' => $this->supervisor->id,
            'collector_id' => $this->collector->id,
            'company_id' => $this->company->id,  // debe haberse inyectado
        ]);
    }

    // ------------------------------------------------------------------
    // DELETE /api/pwa/team/supervisors/{id}/collectors/{id}
    // ------------------------------------------------------------------

    #[Test]
    public function admin_can_unassign_collector_from_supervisor(): void
    {
        $this->supervisor->assignedCollectors()->attach($this->collector->id);

        $this->actingAsAdmin()
            ->deleteJson("/api/pwa/team/supervisors/{$this->supervisor->id}/collectors/{$this->collector->id}")
            ->assertOk();

        $this->assertDatabaseMissing('supervisor_collector', [
            'supervisor_id' => $this->supervisor->id,
            'collector_id' => $this->collector->id,
        ]);
    }

    #[Test]
    public function unassigning_nonexistent_returns_404(): void
    {
        $this->actingAsAdmin()
            ->deleteJson("/api/pwa/team/supervisors/{$this->supervisor->id}/collectors/{$this->collector->id}")
            ->assertNotFound();
    }

    #[Test]
    public function cannot_unassign_collector_from_another_company(): void
    {
        $otherCompany = Company::factory()->create();
        $otherSupervisor = $this->makeUser('supervisor', $otherCompany);
        $otherCollector = $this->makeUser('collector', $otherCompany);

        // Inserción directa para simular dato de otra empresa.
        SupervisorCollector::withoutGlobalScopes()->create([
            'company_id' => $otherCompany->id,
            'supervisor_id' => $otherSupervisor->id,
            'collector_id' => $otherCollector->id,
        ]);

        // Admin de $this->company no puede borrar registros de otra empresa.
        $this->actingAsAdmin()
            ->deleteJson("/api/pwa/team/supervisors/{$otherSupervisor->id}/collectors/{$otherCollector->id}")
            ->assertNotFound();

        $this->assertDatabaseHas('supervisor_collector', [
            'supervisor_id' => $otherSupervisor->id,
            'collector_id' => $otherCollector->id,
        ]);
    }

    // ------------------------------------------------------------------
    // PATCH /api/pwa/team/supervisors/{id}
    // ------------------------------------------------------------------

    #[Test]
    public function admin_can_enable_sees_all_collectors_on_supervisor(): void
    {
        $this->assertFalse($this->supervisor->sees_all_collectors);

        $this->actingAsAdmin()
            ->patchJson("/api/pwa/team/supervisors/{$this->supervisor->id}", [
                'sees_all_collectors' => true,
            ])
            ->assertOk()
            ->assertJsonPath('sees_all_collectors', true);

        $this->assertTrue($this->supervisor->fresh()->sees_all_collectors);
    }

    #[Test]
    public function patching_supervisor_from_another_company_returns_404(): void
    {
        $otherCompany = Company::factory()->create();
        $otherSupervisor = $this->makeUser('supervisor', $otherCompany);

        $this->actingAsAdmin()
            ->patchJson("/api/pwa/team/supervisors/{$otherSupervisor->id}", [
                'sees_all_collectors' => true,
            ])
            ->assertNotFound();
    }

    // ------------------------------------------------------------------
    // GET /api/pwa/team — integración con TeamController
    // ------------------------------------------------------------------

    #[Test]
    public function team_index_returns_unassigned_flag_for_supervisor_without_assignments(): void
    {
        $this->actingAs($this->supervisor, 'sanctum')
            ->getJson('/api/pwa/team')
            ->assertOk()
            ->assertJsonPath('meta.unassigned', true)
            ->assertJsonPath('data', []);
    }

    #[Test]
    public function team_index_returns_only_assigned_collectors_for_supervisor(): void
    {
        $other = $this->makeUser('collector');
        $this->supervisor->assignedCollectors()->attach($this->collector->id);

        $response = $this->actingAs($this->supervisor->fresh(), 'sanctum')
            ->getJson('/api/pwa/team')
            ->assertOk()
            ->assertJsonPath('meta.unassigned', false);

        $ids = collect($response->json('data'))->pluck('id')->all();
        $this->assertContains($this->collector->id, $ids);
        $this->assertNotContains($other->id, $ids);
    }

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

        $response = $this->actingAsAdmin()
            ->getJson('/api/pwa/team')
            ->assertOk()
            ->assertJsonPath('meta.unassigned', false);

        $ids = collect($response->json('data'))->pluck('id')->all();
        $this->assertContains($this->collector->id, $ids);
        $this->assertContains($collector2->id, $ids);
    }
}
