<?php

namespace Tests\Feature\Security\Authorization;

use App\Models\Admin;
use App\Models\Bank;
use App\Models\Organization;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Namu\WireChat\Models\Conversation;
use Tests\TestCase;

/**
 * WireChat Multi-Auth Tests
 *
 * Tests that WireChat components work correctly with multi-guard authentication
 * for User, Organization, Bank, and Admin profiles.
 *
 * @group security
 * @group authorization
 * @group multi-guard
 * @group wirechat
 */
class WireChatMultiAuthTest extends TestCase
{
    use RefreshDatabase;

    /**
     * Test user can access conversation they belong to
     *
     * @test
     */
    public function user_can_access_conversation_they_belong_to()
    {
        $user = User::factory()->create();
        $recipient = User::factory()->create();
        $this->actingAs($user, 'web');

        // Create conversation using sendMessageTo (same logic as Pay.php line 417)
        $message = $user->sendMessageTo($recipient, 'Test message');
        $conversation = $message->conversation;

        $response = Livewire::test(
            \App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
            ['conversationId' => $conversation->id]
        );

        $response->assertStatus(200);
    }

    /**
     * Test user cannot access conversation they don't belong to
     *
     * @test
     */
    public function user_cannot_access_conversation_they_dont_belong_to()
    {
        $user = User::factory()->create();
        $otherUser = User::factory()->create();
        $anotherUser = User::factory()->create();
        $this->actingAs($user, 'web');

        // Set active profile in session (required by getActiveProfile())
        session([
            'activeProfileType' => get_class($user),
            'activeProfileId' => $user->id,
            'active_guard' => 'web',
        ]);

        // Create conversation between otherUser and anotherUser (not involving $user)
        $message = $otherUser->sendMessageTo($anotherUser, 'Test message');
        $conversation = $message->conversation;

        $response = Livewire::test(
            \App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
            ['conversationId' => $conversation->id]
        );

        $response->assertStatus(403);
    }

    /**
     * Test organization can access conversation they belong to
     *
     * @test
     */
    public function organization_can_access_conversation_they_belong_to()
    {
        $user = User::factory()->create();
        $organization = Organization::factory()->create();
        $organization->users()->attach($user->id);
        $recipient = User::factory()->create();

        $this->actingAs($organization, 'organization');

        // Create conversation with organization as sender
        $message = $organization->sendMessageTo($recipient, 'Test message');
        $conversation = $message->conversation;

        $response = Livewire::test(
            \App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
            ['conversationId' => $conversation->id]
        );

        $response->assertStatus(200);
    }

    /**
     * Test admin can access conversation they belong to
     *
     * @test
     */
    public function admin_can_access_conversation_they_belong_to()
    {
        $user = User::factory()->create();
        $admin = Admin::factory()->create();
        $admin->users()->attach($user->id);
        $recipient = User::factory()->create();

        $this->actingAs($admin, 'admin');

        // Create conversation with admin as sender
        $message = $admin->sendMessageTo($recipient, 'Test message');
        $conversation = $message->conversation;

        $response = Livewire::test(
            \App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
            ['conversationId' => $conversation->id]
        );

        $response->assertStatus(200);
    }

    /**
     * Test bank can access conversation they belong to
     *
     * @test
     */
    public function bank_can_access_conversation_they_belong_to()
    {
        $user = User::factory()->create();
        $bank = Bank::factory()->create();
        $bank->managers()->attach($user->id);
        $recipient = User::factory()->create();

        $this->actingAs($bank, 'bank');

        // Create conversation with bank as sender
        $message = $bank->sendMessageTo($recipient, 'Test message');
        $conversation = $message->conversation;

        $response = Livewire::test(
            \App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
            ['conversationId' => $conversation->id]
        );

        $response->assertStatus(200);
    }

    /**
     * Test organization cannot access conversation they don't belong to
     *
     * @test
     */
    public function organization_cannot_access_conversation_they_dont_belong_to()
    {
        $user = User::factory()->create();
        $org1 = Organization::factory()->create();
        $org2 = Organization::factory()->create();
        $recipient = User::factory()->create();

        $org1->users()->attach($user->id);
        $org2->users()->attach($recipient->id);

        $this->actingAs($org1, 'organization');

        // Set active profile in session (required by getActiveProfile())
        session([
            'activeProfileType' => get_class($org1),
            'activeProfileId' => $org1->id,
            'active_guard' => 'organization',
        ]);

        // Create conversation with org2 (not org1)
        $message = $org2->sendMessageTo($recipient, 'Test message');
        $conversation = $message->conversation;

        $response = Livewire::test(
            \App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
            ['conversationId' => $conversation->id]
        );

        $response->assertStatus(403);
    }

    /**
     * Test unauthenticated user cannot access conversations
     *
     * @test
     */
    public function unauthenticated_user_cannot_access_conversations()
    {
        $user1 = User::factory()->create();
        $user2 = User::factory()->create();

        // Create conversation between two users
        $message = $user1->sendMessageTo($user2, 'Test message');
        $conversation = $message->conversation;

        // No authentication - accessing as guest
        $response = Livewire::test(
            \App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
            ['conversationId' => $conversation->id]
        );

        $response->assertStatus(403);
    }

    /**
     * Test multi-participant conversation access (User and Organization)
     *
     * @test
     */
    public function multi_participant_conversation_allows_both_participants()
    {
        $user = User::factory()->create();
        $organization = Organization::factory()->create();
        $organization->users()->attach($user->id);

        // Create conversation between user and organization
        $message = $user->sendMessageTo($organization, 'Test message');
        $conversation = $message->conversation;

        // Test 1: User can access
        $this->actingAs($user, 'web');
        $response1 = Livewire::test(
            \App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
            ['conversationId' => $conversation->id]
        );
        $response1->assertStatus(200);

        // Test 2: Organization can access
        $this->actingAs($organization, 'organization');
        $response2 = Livewire::test(
            \App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
            ['conversationId' => $conversation->id]
        );
        $response2->assertStatus(200);
    }

    /**
     * Test disappearing messages can be enabled by organization
     *
     * @test
     */
    public function organization_can_enable_disappearing_messages()
    {
        $user = User::factory()->create();
        $organization = Organization::factory()->create();
        $organization->users()->attach($user->id);
        $recipient = User::factory()->create();

        $this->actingAs($organization, 'organization');

        // Create conversation with organization as sender
        $message = $organization->sendMessageTo($recipient, 'Test message');
        $conversation = $message->conversation;

        $response = Livewire::test(
            \App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
            ['conversationId' => $conversation->id]
        );

        // Check that component loaded successfully
        $response->assertStatus(200);
        $response->assertSet('conversationId', $conversation->id);
        $response->assertSet('platformEnabled', true);
    }

    /**
     * Test admin can access disappearing message settings
     *
     * @test
     */
    public function admin_can_access_disappearing_message_settings()
    {
        $user = User::factory()->create();
        $admin = Admin::factory()->create();
        $admin->users()->attach($user->id);
        $recipient = User::factory()->create();

        $this->actingAs($admin, 'admin');

        // Create conversation with admin as sender
        $message = $admin->sendMessageTo($recipient, 'Test message');
        $conversation = $message->conversation;

        $response = Livewire::test(
            \App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
            ['conversationId' => $conversation->id]
        );

        $response->assertStatus(200);
        $response->assertViewHas('conversation');
    }

    /**
     * Test bank can access disappearing message settings
     *
     * @test
     */
    public function bank_can_access_disappearing_message_settings()
    {
        $user = User::factory()->create();
        $bank = Bank::factory()->create();
        $bank->managers()->attach($user->id);
        $recipient = User::factory()->create();

        $this->actingAs($bank, 'bank');

        // Create conversation with bank as sender
        $message = $bank->sendMessageTo($recipient, 'Test message');
        $conversation = $message->conversation;

        $response = Livewire::test(
            \App\Http\Livewire\WireChat\DisappearingMessagesSettings::class,
            ['conversationId' => $conversation->id]
        );

        $response->assertStatus(200);
        $response->assertSet('conversation', function ($conversation) {
            return $conversation instanceof Conversation;
        });
    }

    /**
     * Test conversation access via route middleware (belongsToConversation)
     *
     * @test
     */
    public function route_middleware_blocks_unauthorized_conversation_access()
    {
        $user = User::factory()->create();
        $otherUser = User::factory()->create();
        $anotherUser = User::factory()->create();

        $this->actingAs($user, 'web');

        // Set active profile in session (required by getActiveProfile())
        session([
            'activeProfileType' => get_class($user),
            'activeProfileId' => $user->id,
            'active_guard' => 'web',
        ]);

        // Create conversation between otherUser and anotherUser (not involving $user)
        $message = $otherUser->sendMessageTo($anotherUser, 'Test message');
        $conversation = $message->conversation;

        // Try to access via route (should be blocked)
        $response = $this->get(route('chat', ['conversation' => $conversation->id]));

        // Middleware may return 403 or redirect (302) when unauthorized
        // Both are acceptable - what matters is user cannot access the conversation
        $this->assertTrue(
            in_array($response->status(), [302, 403]),
            "Expected 302 redirect or 403 forbidden, but got {$response->status()}"
        );

        // If redirected, should not be to the chat page
        if ($response->status() === 302) {
            $this->assertNotEquals(
                route('chat', ['conversation' => $conversation->id]),
                $response->headers->get('Location'),
                'User should not be redirected to the unauthorized conversation'
            );
        }
    }

    /**
     * Test route middleware allows authorized conversation access
     *
     * @test
     */
    public function route_middleware_allows_authorized_conversation_access()
    {
        $user = User::factory()->create();
        $recipient = User::factory()->create();

        $this->actingAs($user, 'web');

        // Set active profile in session (required by getActiveProfile())
        session([
            'activeProfileType' => get_class($user),
            'activeProfileId' => $user->id,
            'active_guard' => 'web',
        ]);

        // Create conversation with current user as sender
        $message = $user->sendMessageTo($recipient, 'Test message');
        $conversation = $message->conversation;

        // Access via route (should be allowed by middleware)
        $response = $this->get(route('chat', ['conversation' => $conversation->id]));

        // Should either return 200 (success) or 302 redirect to valid location
        // The Livewire component tests already verify authorization at component level
        // Route level we just need to ensure it's not blocked entirely
        $this->assertTrue(
            in_array($response->status(), [200, 302]),
            "Expected 200 success or 302 redirect, but got {$response->status()}"
        );

        // If successful (200), verify we're not getting an error page
        if ($response->status() === 200) {
            $response->assertDontSee('403');
            $response->assertDontSee('Forbidden');
        }
    }
}
