<?php

use App\Livewire\ManageUsers;
use App\Mail\InvitationMail;
use App\Models\Invitation;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Mail;
use Livewire\Livewire;

uses(RefreshDatabase::class);

beforeEach(function () {
    // Create test users
    $this->admin = User::factory()->create(['is_admin' => true]);
    $this->user = User::factory()->create(['is_admin' => false]);
});

describe('ManageUsers Component', function () {
    
    describe('invitation creation', function () {
        
        it('allows admins to create invitations', function () {
            $this->actingAs($this->admin);
            
            Livewire::test(ManageUsers::class)
                ->set('invite_email', 'test@example.com')
                ->call('inviteUser')
                ->assertHasNoErrors();
                
            $this->assertDatabaseHas('invitations', [
                'email' => 'test@example.com',
                'invited_by' => $this->admin->id,
            ]);
        });
        
        it('prevents non-admins from creating invitations', function () {
            // Test authorization at the method level without rendering view
            $this->actingAs($this->user);
            
            $component = new ManageUsers();
            $component->invite_email = 'test@example.com';
            
            $this->expectException(\Illuminate\Auth\Access\AuthorizationException::class);
            $component->inviteUser();
        });
        
        it('validates email format when creating invitations', function () {
            $this->actingAs($this->admin);
            
            Livewire::test(ManageUsers::class)
                ->set('invite_email', 'invalid-email')
                ->call('inviteUser')
                ->assertHasErrors('invite_email');
        });
        
        it('generates a random code and sets expiration when creating invitations', function () {
            $this->actingAs($this->admin);
            
            Livewire::test(ManageUsers::class)
                ->set('invite_email', 'test@example.com')
                ->call('inviteUser');
                
            $invitation = Invitation::where('email', 'test@example.com')->first();
            
            expect($invitation->code)->toHaveLength(50);
            expect($invitation->expires_at)->not()->toBeNull();
            expect($invitation->invited_by)->toBe($this->admin->id);
        });
    });
    
    describe('email sending', function () {
        
        it('sends email when checkbox is checked', function () {
            Mail::fake();
            $this->actingAs($this->admin);
            
            Livewire::test(ManageUsers::class)
                ->set('invite_email', 'test@example.com')
                ->set('send_email', true)
                ->call('inviteUser');
                
            $invitation = Invitation::where('email', 'test@example.com')->first();
            
            Mail::assertSent(InvitationMail::class, function ($mail) use ($invitation) {
                return $mail->hasTo('test@example.com') && 
                       $mail->invitation->id === $invitation->id;
            });
        });
        
        it('does not send email when checkbox is unchecked', function () {
            Mail::fake();
            $this->actingAs($this->admin);
            
            Livewire::test(ManageUsers::class)
                ->set('invite_email', 'test@example.com')
                ->set('send_email', false)
                ->call('inviteUser');
                
            Mail::assertNotSent(InvitationMail::class);
        });
        
        it('sends email to the correct recipient', function () {
            Mail::fake();
            $this->actingAs($this->admin);
            
            $testEmail = 'specific@example.com';
            
            Livewire::test(ManageUsers::class)
                ->set('invite_email', $testEmail)
                ->set('send_email', true)
                ->call('inviteUser');
                
            Mail::assertSent(InvitationMail::class, function ($mail) use ($testEmail) {
                return $mail->hasTo($testEmail);
            });
            
            Mail::assertNotSent(InvitationMail::class, function ($mail) {
                return $mail->hasTo('wrong@example.com');
            });
        });
    });
    
    describe('invitation deletion', function () {
        
        it('allows admins to delete pending invitations', function () {
            $this->actingAs($this->admin);
            
            $invitation = Invitation::factory()->create([
                'accepted_at' => null, // Pending invitation
            ]);
            
            Livewire::test(ManageUsers::class)
                ->call('deleteInvitation', $invitation)
                ->assertHasNoErrors();
                
            $this->assertDatabaseMissing('invitations', [
                'id' => $invitation->id,
            ]);
        });
        
        it('prevents non-admins from deleting invitations', function () {
            // Test authorization at the method level without rendering view
            $this->actingAs($this->user);
            
            $invitation = Invitation::factory()->create([
                'accepted_at' => null,
            ]);
            
            $component = new ManageUsers();
            
            $this->expectException(\Illuminate\Auth\Access\AuthorizationException::class);
            $component->deleteInvitation($invitation);
        });
        
        it('prevents deletion of accepted invitations', function () {
            $this->actingAs($this->admin);
            
            $invitation = Invitation::factory()->create([
                'accepted_at' => now(), // Accepted invitation
            ]);
            
            Livewire::test(ManageUsers::class)
                ->call('deleteInvitation', $invitation)
                ->assertHasNoErrors(); // Component handles this gracefully
                
            // Invitation should still exist
            $this->assertDatabaseHas('invitations', [
                'id' => $invitation->id,
            ]);
        });
    });
    
    describe('component state management', function () {
        
        it('resets form fields after successful invitation creation', function () {
            $this->actingAs($this->admin);
            
            $component = Livewire::test(ManageUsers::class)
                ->set('invite_email', 'test@example.com')
                ->set('send_email', true)
                ->call('inviteUser');
                
            expect($component->get('invite_email'))->toBe('');
            expect($component->get('send_email'))->toBe(false);
        });
        
        it('updates invitations collection after creation', function () {
            $this->actingAs($this->admin);
            
            $component = Livewire::test(ManageUsers::class);
            $initialCount = $component->get('invitations')->count();
            
            $component
                ->set('invite_email', 'test@example.com')
                ->call('inviteUser');
                
            expect($component->get('invitations')->count())->toBe($initialCount + 1);
        });
        
        it('shows success message after invitation creation', function () {
            $this->actingAs($this->admin);
            
            Livewire::test(ManageUsers::class)
                ->set('invite_email', 'test@example.com')
                ->set('send_email', false)
                ->call('inviteUser')
                ->assertHasNoErrors();
                
            // Verify the invitation was created
            $this->assertDatabaseHas('invitations', [
                'email' => 'test@example.com',
            ]);
        });
        
        it('tracks email sending status correctly', function () {
            Mail::fake();
            $this->actingAs($this->admin);
            
            Livewire::test(ManageUsers::class)
                ->set('invite_email', 'test@example.com')
                ->set('send_email', true)
                ->call('inviteUser')
                ->assertHasNoErrors();
                
            // Verify the invitation was created
            $this->assertDatabaseHas('invitations', [
                'email' => 'test@example.com',
            ]);
            
            // Verify email was sent
            Mail::assertSent(InvitationMail::class);
        });
    });
    
    describe('authorization integration', function () {
        
        it('loads data for admin users', function () {
            $this->actingAs($this->admin);
            $adminComponent = Livewire::test(ManageUsers::class);
            expect($adminComponent->get('users'))->not()->toBeNull();
            expect($adminComponent->get('invitations'))->not()->toBeNull();
        });
    });
    
    describe('user role management', function () {
        
        it('allows admins to change user roles', function () {
            $this->actingAs($this->admin);
            $targetUser = User::factory()->create(['is_admin' => false]);
            
            Livewire::test(ManageUsers::class)
                ->call('changeUserRole', $targetUser, 'admin')
                ->assertHasNoErrors();
                
            expect($targetUser->fresh()->is_admin)->toBe(true);
        });
        
        it('prevents admins from demoting themselves', function () {
            $this->actingAs($this->admin);
            
            Livewire::test(ManageUsers::class)
                ->call('changeUserRole', $this->admin, 'user')
                ->assertHasNoErrors();
                
            // Verify the admin status wasn't changed
            expect($this->admin->fresh()->is_admin)->toBe(true);
        });
        
        it('allows admins to delete other users', function () {
            $this->actingAs($this->admin);
            $targetUser = User::factory()->create();
            
            Livewire::test(ManageUsers::class)
                ->call('deleteUser', $targetUser)
                ->assertHasNoErrors();
                
            $this->assertDatabaseMissing('users', [
                'id' => $targetUser->id,
            ]);
        });
    });
});