<?php

namespace Tests\Feature;

use App\Enums\UserAccountStatus;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Spatie\Permission\Models\Role;
use Tests\TestCase;

/**
 * Regression tests to ensure tenant users are properly excluded from employee lists.
 *
 * These tests verify that the employeesOnly() and activeEmployees() scopes
 * correctly filter out non-employee roles (tenant, super_admin, client).
 */
class TenantExclusionTest extends TestCase
{
    use RefreshDatabase;

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

        // Ensure roles exist
        Role::firstOrCreate(['name' => 'tenant', 'guard_name' => 'web']);
        Role::firstOrCreate(['name' => 'super_admin', 'guard_name' => 'web']);
        Role::firstOrCreate(['name' => 'client', 'guard_name' => 'web']);
        Role::firstOrCreate(['name' => 'employee', 'guard_name' => 'web']);
    }

    public function test_employees_only_scope_excludes_tenants(): void
    {
        // Create a tenant user
        $tenant = User::factory()->create(['status' => UserAccountStatus::ACTIVE]);
        $tenant->assignRole('tenant');

        // Create a regular employee
        $employee = User::factory()->create(['status' => UserAccountStatus::ACTIVE]);
        $employee->assignRole('employee');

        // Query using employeesOnly scope
        $results = User::employeesOnly()->get();

        // Assert tenant is not in results
        $this->assertFalse($results->contains('id', $tenant->id), 'Tenant should be excluded from employeesOnly scope');

        // Assert employee is in results
        $this->assertTrue($results->contains('id', $employee->id), 'Employee should be included in employeesOnly scope');
    }

    public function test_employees_only_scope_excludes_super_admin(): void
    {
        // Create a super admin user
        $superAdmin = User::factory()->create(['status' => UserAccountStatus::ACTIVE]);
        $superAdmin->assignRole('super_admin');

        // Create a regular employee
        $employee = User::factory()->create(['status' => UserAccountStatus::ACTIVE]);
        $employee->assignRole('employee');

        // Query using employeesOnly scope
        $results = User::employeesOnly()->get();

        // Assert super_admin is not in results
        $this->assertFalse($results->contains('id', $superAdmin->id), 'Super admin should be excluded from employeesOnly scope');

        // Assert employee is in results
        $this->assertTrue($results->contains('id', $employee->id), 'Employee should be included in employeesOnly scope');
    }

    public function test_employees_only_scope_excludes_clients(): void
    {
        // Create a client user
        $client = User::factory()->create(['status' => UserAccountStatus::ACTIVE]);
        $client->assignRole('client');

        // Create a regular employee
        $employee = User::factory()->create(['status' => UserAccountStatus::ACTIVE]);
        $employee->assignRole('employee');

        // Query using employeesOnly scope
        $results = User::employeesOnly()->get();

        // Assert client is not in results
        $this->assertFalse($results->contains('id', $client->id), 'Client should be excluded from employeesOnly scope');

        // Assert employee is in results
        $this->assertTrue($results->contains('id', $employee->id), 'Employee should be included in employeesOnly scope');
    }

    public function test_active_employees_scope_excludes_inactive_users(): void
    {
        // Create an inactive employee
        $inactiveEmployee = User::factory()->create(['status' => UserAccountStatus::INACTIVE]);
        $inactiveEmployee->assignRole('employee');

        // Create an active employee
        $activeEmployee = User::factory()->create(['status' => UserAccountStatus::ACTIVE]);
        $activeEmployee->assignRole('employee');

        // Query using activeEmployees scope
        $results = User::activeEmployees()->get();

        // Assert inactive employee is not in results
        $this->assertFalse($results->contains('id', $inactiveEmployee->id), 'Inactive employee should be excluded from activeEmployees scope');

        // Assert active employee is in results
        $this->assertTrue($results->contains('id', $activeEmployee->id), 'Active employee should be included in activeEmployees scope');
    }

    public function test_active_employees_scope_combines_role_and_status_filtering(): void
    {
        // Create various user types
        $activeTenant = User::factory()->create(['status' => UserAccountStatus::ACTIVE]);
        $activeTenant->assignRole('tenant');

        $inactiveTenant = User::factory()->create(['status' => UserAccountStatus::INACTIVE]);
        $inactiveTenant->assignRole('tenant');

        $activeEmployee = User::factory()->create(['status' => UserAccountStatus::ACTIVE]);
        $activeEmployee->assignRole('employee');

        $inactiveEmployee = User::factory()->create(['status' => UserAccountStatus::INACTIVE]);
        $inactiveEmployee->assignRole('employee');

        // Query using activeEmployees scope
        $results = User::activeEmployees()->get();

        // Assert only active employee is in results
        $this->assertFalse($results->contains('id', $activeTenant->id), 'Active tenant should be excluded');
        $this->assertFalse($results->contains('id', $inactiveTenant->id), 'Inactive tenant should be excluded');
        $this->assertFalse($results->contains('id', $inactiveEmployee->id), 'Inactive employee should be excluded');
        $this->assertTrue($results->contains('id', $activeEmployee->id), 'Active employee should be included');

        // Verify only one user in results
        $this->assertCount(1, $results, 'Only one user (active employee) should be in results');
    }

    public function test_employees_only_scope_allows_additional_role_exclusions(): void
    {
        // Create users with different roles
        $manager = User::factory()->create(['status' => UserAccountStatus::ACTIVE]);
        $manager->assignRole('employee'); // Manager is also an employee

        $contractor = User::factory()->create(['status' => UserAccountStatus::ACTIVE]);
        // Assume contractor has no specific role yet

        $employee = User::factory()->create(['status' => UserAccountStatus::ACTIVE]);
        $employee->assignRole('employee');

        // Query with additional role exclusion
        $results = User::employeesOnly(['manager'])->get();

        // All should be included since 'manager' role doesn't exist in our test setup
        // This tests that the method accepts additional exclusion arrays
        $this->assertGreaterThanOrEqual(2, $results->count());
    }

    public function test_scopes_are_chainable(): void
    {
        // Create employees with different first names for filtering
        $employee1 = User::factory()->create([
            'status' => UserAccountStatus::ACTIVE,
            'first_name' => 'John',
        ]);
        $employee1->assignRole('employee');

        $employee2 = User::factory()->create([
            'status' => UserAccountStatus::ACTIVE,
            'first_name' => 'Jane',
        ]);
        $employee2->assignRole('employee');

        $tenant = User::factory()->create([
            'status' => UserAccountStatus::ACTIVE,
            'first_name' => 'John',
        ]);
        $tenant->assignRole('tenant');

        // Chain scope with additional where clause
        $results = User::activeEmployees()->where('first_name', 'John')->get();

        // Should include only employee1 (John who is an employee)
        $this->assertTrue($results->contains('id', $employee1->id), 'John (employee) should be included');
        $this->assertFalse($results->contains('id', $employee2->id), 'Jane should be excluded (different name)');
        $this->assertFalse($results->contains('id', $tenant->id), 'John (tenant) should be excluded (tenant role)');
    }
}
