<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
use App\Models\Property;
use App\Models\Customer;
use App\Models\Project;
use App\Models\Block;
use Exception;

class BookingTransactionTest extends TestCase
{
    use RefreshDatabase;

    public function test_booking_uses_pessimistic_locking_to_prevent_double_booking()
    {
        // Scaffold required data
        $project = Project::create(['name' => 'DHA Phase 6', 'status' => 'Active']);
        $block = Block::create(['project_id' => $project->id, 'name' => 'Block A']);
        
        $property = Property::create([
            'block_id' => $block->id,
            'plot_number' => '101',
            'property_type' => 'Residential',
            'size' => '5 Marla',
            'base_price' => 5000000,
            'status' => 'Available',
        ]);

        $customer1 = Customer::create(['full_name' => 'Ali', 'phone' => '123456789']);
        $customer2 = Customer::create(['full_name' => 'Umer', 'phone' => '987654321']);

        // Simulate Booking Process using Transactions
        try {
            DB::transaction(function () use ($property, $customer1) {
                // lockForUpdate prevents concurrent modifications
                $lockedProperty = Property::where('id', $property->id)->lockForUpdate()->first();
                
                if ($lockedProperty->status !== 'Available') {
                    throw new Exception("Property is already booked.");
                }

                $lockedProperty->status = 'Booked';
                $lockedProperty->save();

                // Here we would also create Invoices/Installments
            });
        } catch (\Exception $e) {
            //
        }

        // Assert property is booked by customer1
        $this->assertEquals('Booked', $property->fresh()->status);

        // Customer 2 attempts to book the same property
        $exceptionThrown = false;
        try {
            DB::transaction(function () use ($property, $customer2) {
                $lockedProperty = Property::where('id', $property->id)->lockForUpdate()->first();
                
                if ($lockedProperty->status !== 'Available') {
                    throw new Exception("Property is already booked.");
                }

                $lockedProperty->status = 'Booked';
                $lockedProperty->save();
            });
        } catch (\Exception $e) {
            $exceptionThrown = true;
        }

        // Assert the second booking failed and threw exception
        $this->assertTrue($exceptionThrown);
    }
}
