<?php

namespace Tests\Feature\Security\IDOR;

use App\Models\User;
use App\Models\Organization;
use App\Models\Bank;
use App\Models\Admin;
use App\Models\Post;
use App\Models\Transaction;
use App\Models\Account;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

/**
 * Profile Access IDOR (Insecure Direct Object Reference) Tests
 *
 * Tests that users cannot access resources belonging to other users
 * by manipulating IDs in requests.
 *
 * @group security
 * @group idor
 * @group critical
 */
class ProfileAccessIDORTest extends TestCase
{
    use RefreshDatabase;

    /**
     * Test user cannot view another user's private profile data via ID manipulation
     *
     * @test
     */
    public function user_cannot_view_another_users_private_profile_via_id()
    {
        // Arrange: Create two users
        $user1 = User::factory()->create();
        $user2 = User::factory()->create();

        // Act: Login as user1, try to access user2's profile edit page
        $response = $this->actingAs($user1)
            ->get(route('profile.edit'));

        // Try to load user2's data by manipulating session
        session(['activeProfileId' => $user2->id]);

        $response2 = $this->actingAs($user1)
            ->get(route('profile.edit'));

        // Assert: Should not be able to see user2's private data
        // This is a basic check - deeper validation would require inspecting response content
        $this->assertTrue(true); // Placeholder for more specific assertions
    }

    /**
     * Test user cannot access another user's account balance via ID manipulation
     *
     * @test
     */
    public function user_cannot_access_another_users_account_balance()
    {
        // Arrange: Create two users with accounts
        $user1 = User::factory()->create();
        $user2 = User::factory()->create();

        $account1 = Account::factory()->create([
            'accountable_type' => User::class,
            'accountable_id' => $user1->id,
        ]);

        $account2 = Account::factory()->create([
            'accountable_type' => User::class,
            'accountable_id' => $user2->id,
        ]);

        // Create a transaction for user2
        Transaction::factory()->create([
            'from_account_id' => $account2->id,
            'to_account_id' => $account1->id,
            'amount' => 100,
        ]);

        // Act: Login as user1
        $this->actingAs($user1);

        // Try to access account page
        $response = $this->get(route('accounts'));

        // Assert: user1 should only see their own account balance
        // Should NOT see user2's account details
        $response->assertSuccessful();

        // The response should not contain user2's account ID or specific balance
        // (This would require more specific assertions based on actual response structure)
    }

    /**
     * Test user cannot view another user's transaction history via ID manipulation
     *
     * @test
     */
    public function user_cannot_view_another_users_transaction_history()
    {
        // Arrange: Create two users with transactions
        $user1 = User::factory()->create();
        $user2 = User::factory()->create();

        $account1 = Account::factory()->create([
            'accountable_type' => User::class,
            'accountable_id' => $user1->id,
        ]);

        $account2 = Account::factory()->create([
            'accountable_type' => User::class,
            'accountable_id' => $user2->id,
        ]);

        // Create transaction between two different users (not involving user1)
        $user3 = User::factory()->create();
        $account3 = Account::factory()->create([
            'accountable_type' => User::class,
            'accountable_id' => $user3->id,
        ]);

        $privateTransaction = Transaction::factory()->create([
            'from_account_id' => $account2->id,
            'to_account_id' => $account3->id,
            'amount' => 500,
            'description' => 'Private transaction between user2 and user3',
        ]);

        // Act: Login as user1 and try to view transaction history
        $response = $this->actingAs($user1)
            ->get(route('transactions'));

        // Assert: Should only see transactions involving user1
        $response->assertSuccessful();

        // Try to access specific transaction by ID manipulation
        $response2 = $this->actingAs($user1)
            ->get(route('statement', ['transactionId' => $privateTransaction->id]));

        // Assert: Should not be able to access transaction details
        // (Implementation should check ownership)
        // For now, verify the route is accessible but doesn't leak data
    }

    /**
     * Test user cannot edit another user's post via ID manipulation
     *
     * @test
     */
    public function user_cannot_edit_another_users_post()
    {
        // Arrange: Create two users with posts
        $user1 = User::factory()->create();
        $user2 = User::factory()->create();

        $user1Post = Post::factory()->create([
            'profile_id' => $user1->id,
            'profile_type' => User::class,
        ]);

        $user2Post = Post::factory()->create([
            'profile_id' => $user2->id,
            'profile_type' => User::class,
        ]);

        // Act: Login as user1 and try to edit user2's post
        $this->actingAs($user1);

        // Try to access edit page for user2's post
        $response = $this->get("/posts/{$user2Post->id}/edit");

        // Assert: Should be denied (403) or redirected
        // Proper authorization should prevent accessing edit page
        $this->assertTrue(
            $response->status() === 403 || $response->status() === 302,
            "User should not be able to edit another user's post"
        );
    }

    /**
     * Test user cannot access organization dashboard they're not linked to
     *
     * @test
     */
    public function user_cannot_access_organization_dashboard_without_link()
    {
        // Arrange: Create user and two organizations
        $user = User::factory()->create();
        $org1 = Organization::factory()->create();
        $org2 = Organization::factory()->create();

        // Link user to org1 only
        $user->organizations()->attach($org1->id);

        // Act: Login as user
        $this->actingAs($user);

        // Try to switch to org2 (not linked)
        session(['activeProfileType' => 'App\\Models\\Organization']);
        session(['activeProfileId' => $org2->id]); // IDOR attempt
        session(['active_guard' => 'organization']);

        // Try to access dashboard
        $response = $this->get(route('dashboard'));

        // Assert: Should not have access to org2's dashboard
        // The getActiveProfile() helper should validate the link
    }

    /**
     * Test user cannot access bank dashboard they're not linked to
     *
     * @test
     */
    public function user_cannot_access_bank_dashboard_without_link()
    {
        // Arrange: Create user and two banks
        $user = User::factory()->create();
        $bank1 = Bank::factory()->create();
        $bank2 = Bank::factory()->create();

        // Link user to bank1 only
        $user->banks()->attach($bank1->id);

        // Act: Login as user
        $this->actingAs($user);

        // Try to switch to bank2 (not linked)
        session(['activeProfileType' => 'App\\Models\\Bank']);
        session(['activeProfileId' => $bank2->id]); // IDOR attempt
        session(['active_guard' => 'bank']);

        // Try to access protected bank functionality
        $response = $this->get(route('dashboard'));

        // Assert: Should not have access to bank2
        // Middleware or profile validation should prevent this
    }

    /**
     * Test user cannot access admin panel by ID manipulation
     *
     * @test
     */
    public function regular_user_cannot_access_admin_panel()
    {
        // Arrange: Create regular user and admin
        $user = User::factory()->create();
        $admin = Admin::factory()->create();

        // Act: Login as regular user
        $this->actingAs($user);

        // Try to manipulate session to become admin
        session(['activeProfileType' => 'App\\Models\\Admin']);
        session(['activeProfileId' => $admin->id]); // IDOR attempt
        session(['active_guard' => 'admin']);

        // Try to access admin dashboard
        $response = $this->get('/admin/dashboard');

        // Assert: Should be denied
        $this->assertTrue(
            $response->status() === 403 || $response->status() === 302 || $response->status() === 404,
            "Regular user should not access admin panel"
        );
    }

    /**
     * Test API endpoints validate ownership (if applicable)
     *
     * @test
     */
    public function api_endpoints_validate_resource_ownership()
    {
        // Arrange: Create two users
        $user1 = User::factory()->create();
        $user2 = User::factory()->create();

        // Act: Login as user1
        $this->actingAs($user1);

        // Try to access user2's data via API (if API exists)
        // This is a placeholder test - actual implementation depends on API structure

        // Assert: Proper 403 or ownership validation
        $this->assertTrue(true); // Placeholder
    }
}
