<?php

namespace Tests\Tenant;

use App\Events\VehiclePriceChangedDuringStockSync;
use App\Facades\Settings;
use App\Listeners\RefreshFinanceOnVehiclePriceChange;
use App\Traits\StockSyncTraits;
use Database\Seeders\Tenant\FinanceProductRestrictionSetting;
use Database\Seeders\Tenant\FinanceSettingSeeder;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Event;
use Mtc\MercuryDataModels\Finance\Jobs\FetchVehicleFinanceData;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleFinance;
use Tests\TenantTestCase;

class RefreshFinanceOnVehiclePriceChangeTest extends TenantTestCase
{
    public function testDeletesCachedFinanceQuotesOnPriceChange(): void
    {
        $vehicle = Vehicle::factory()->create([
            'price' => 10000,
            'monthly_price' => 199,
            'monthly_cost_type' => 'pcp',
        ]);

        // Create cached finance quotes from provider
        VehicleFinance::factory()->create([
            'vehicle_id' => $vehicle->id,
            'provider' => 'codeweavers',
            'monthly_price' => 199,
        ]);
        VehicleFinance::factory()->create([
            'vehicle_id' => $vehicle->id,
            'provider' => 'codeweavers',
            'monthly_price' => 250,
        ]);

        $this->assertCount(2, $vehicle->financeExamples);

        $event = new VehiclePriceChangedDuringStockSync($vehicle, 'test-provider');
        $listener = new RefreshFinanceOnVehiclePriceChange();
        $listener->handle($event);

        $vehicle->refresh();

        // Assert cached quotes are deleted
        $this->assertCount(0, $vehicle->financeExamples);

        // Assert vehicle finance fields are cleared
        $this->assertNull($vehicle->monthly_price);
        $this->assertNull($vehicle->monthly_cost_type);
    }

    public function testPreservesManualFinanceEntriesOnPriceChange(): void
    {
        $vehicle = Vehicle::factory()->create([
            'price' => 10000,
            'monthly_price' => 199,
            'monthly_cost_type' => 'pcp',
        ]);

        // Create a manual finance entry
        VehicleFinance::factory()->create([
            'vehicle_id' => $vehicle->id,
            'provider' => 'manual',
            'monthly_price' => 180,
        ]);

        // Create cached finance quotes from provider
        VehicleFinance::factory()->create([
            'vehicle_id' => $vehicle->id,
            'provider' => 'codeweavers',
            'monthly_price' => 199,
        ]);

        $this->assertCount(2, $vehicle->financeExamples);

        $event = new VehiclePriceChangedDuringStockSync($vehicle, 'test-provider');
        $listener = new RefreshFinanceOnVehiclePriceChange();
        $listener->handle($event);

        $vehicle->refresh();

        // Assert only manual entry is preserved
        $this->assertCount(1, $vehicle->financeExamples);
        $this->assertEquals('manual', $vehicle->financeExamples->first()->provider);
    }

    public function testRequestsNewQuotesWhenProviderEnabled(): void
    {
        Bus::fake();

        $this->seed(FinanceSettingSeeder::class);
        $this->seed(FinanceProductRestrictionSetting::class);
        Settings::update('finance-codeweavers-enabled', true);
        Settings::update('finance-codeweavers-api_key', 'test-key');
        Settings::update('finance-codeweavers-username', 'test-user');
        Settings::update('finance-codeweavers-password', 'test-pass');
        Settings::update('finance-batch-update', false);

        $vehicle = Vehicle::factory()->create([
            'price' => 10000,
            'registration_number' => 'AB12CDE',
            'cap_id' => '12345',
        ]);

        $event = new VehiclePriceChangedDuringStockSync($vehicle, 'test-provider');
        $listener = new RefreshFinanceOnVehiclePriceChange();
        $listener->handle($event);

        Bus::assertDispatched(FetchVehicleFinanceData::class);
    }

    public function testDoesNotRequestNewQuotesWhenBatchUpdateEnabled(): void
    {
        Bus::fake();

        $this->seed(FinanceSettingSeeder::class);
        $this->seed(FinanceProductRestrictionSetting::class);
        Settings::update('finance-codeweavers-enabled', true);
        Settings::update('finance-codeweavers-api_key', 'test-key');
        Settings::update('finance-codeweavers-username', 'test-user');
        Settings::update('finance-codeweavers-password', 'test-pass');
        Settings::update('finance-batch-update', true);

        $vehicle = Vehicle::factory()->create([
            'price' => 10000,
            'registration_number' => 'AB12CDE',
            'cap_id' => '12345',
        ]);

        $event = new VehiclePriceChangedDuringStockSync($vehicle, 'test-provider');
        $listener = new RefreshFinanceOnVehiclePriceChange();
        $listener->handle($event);

        Bus::assertNotDispatched(FetchVehicleFinanceData::class);
    }

    public function testSkipsEverythingWhenStopFinanceSyncIsTrue(): void
    {
        Bus::fake();

        $this->seed(FinanceSettingSeeder::class);
        $this->seed(FinanceProductRestrictionSetting::class);
        Settings::update('finance-codeweavers-enabled', true);
        Settings::update('finance-codeweavers-api_key', 'test-key');
        Settings::update('finance-codeweavers-username', 'test-user');
        Settings::update('finance-codeweavers-password', 'test-pass');
        Settings::update('finance-batch-update', false);

        $vehicle = Vehicle::factory()->create([
            'price' => 10000,
            'monthly_price' => 199,
            'monthly_cost_type' => 'pcp',
            'registration_number' => 'AB12CDE',
            'cap_id' => '12345',
            'data' => ['stop_finance_sync' => true],
        ]);

        VehicleFinance::factory()->create([
            'vehicle_id' => $vehicle->id,
            'provider' => 'codeweavers',
            'monthly_price' => 199,
        ]);

        $event = new VehiclePriceChangedDuringStockSync($vehicle, 'test-provider');
        $listener = new RefreshFinanceOnVehiclePriceChange();
        $listener->handle($event);

        $vehicle->refresh();

        // Existing provider examples are preserved (user has opted out of sync)
        $this->assertCount(1, $vehicle->financeExamples);

        // Vehicle finance fields are NOT cleared
        $this->assertEquals(199, $vehicle->monthly_price);
        $this->assertEquals('pcp', $vehicle->monthly_cost_type);

        // No new quotes requested
        Bus::assertNotDispatched(FetchVehicleFinanceData::class);
    }

    /**
     * Test that event IS dispatched when price actually changes to a different value
     */
    public function testEventDispatchedWhenPriceActuallyChanges(): void
    {
        $vehicle = Vehicle::factory()->create(['price' => 10000.00]);
        $originalPrice = $vehicle->price;

        // Simulate stock sync updating price to different value
        $vehicle->price = 15000.00;
        $vehicle->save();

        Event::fake([VehiclePriceChangedDuringStockSync::class]);

        $this->dispatchPriceChangeEvent($vehicle, 'test-provider', $originalPrice);

        Event::assertDispatched(VehiclePriceChangedDuringStockSync::class);
    }

    /**
     * Test that event IS NOT dispatched when price is set to same value as string
     * (e.g., stock sync sends '10000' string when DB has 10000.00 decimal)
     */
    public function testEventNotDispatchedWhenPriceSetToSameValueAsString(): void
    {
        $vehicle = Vehicle::factory()->create(['price' => 10000.00]);
        $originalPrice = $vehicle->price;

        // Simulate stock sync setting price as string with same numeric value
        $vehicle->price = '10000';
        $vehicle->save();

        Event::fake([VehiclePriceChangedDuringStockSync::class]);

        $this->dispatchPriceChangeEvent($vehicle, 'test-provider', $originalPrice);

        Event::assertNotDispatched(VehiclePriceChangedDuringStockSync::class);
    }

    /**
     * Test that event IS NOT dispatched when price is set to same value as integer
     */
    public function testEventNotDispatchedWhenPriceSetToSameValueAsInteger(): void
    {
        $vehicle = Vehicle::factory()->create(['price' => 10000.00]);
        $originalPrice = $vehicle->price;

        // Simulate stock sync setting price as integer with same numeric value
        $vehicle->price = 10000;
        $vehicle->save();

        Event::fake([VehiclePriceChangedDuringStockSync::class]);

        $this->dispatchPriceChangeEvent($vehicle, 'test-provider', $originalPrice);

        Event::assertNotDispatched(VehiclePriceChangedDuringStockSync::class);
    }

    /**
     * Test that event IS NOT dispatched when price hasn't changed at all
     */
    public function testEventNotDispatchedWhenPriceNotChanged(): void
    {
        $vehicle = Vehicle::factory()->create(['price' => 10000.00]);
        $originalPrice = $vehicle->price;

        // Only update another field, not price
        $vehicle->title = 'Updated Title';
        $vehicle->save();

        Event::fake([VehiclePriceChangedDuringStockSync::class]);

        $this->dispatchPriceChangeEvent($vehicle, 'test-provider', $originalPrice);

        Event::assertNotDispatched(VehiclePriceChangedDuringStockSync::class);
    }

    /**
     * Test that event IS dispatched when price changes from null to a value
     */
    public function testEventDispatchedWhenPriceChangesFromNull(): void
    {
        $vehicle = Vehicle::factory()->create(['price' => null]);
        $originalPrice = $vehicle->price;

        $vehicle->price = 10000.00;
        $vehicle->save();

        Event::fake([VehiclePriceChangedDuringStockSync::class]);

        $this->dispatchPriceChangeEvent($vehicle, 'test-provider', $originalPrice);

        Event::assertDispatched(VehiclePriceChangedDuringStockSync::class);
    }

    /**
     * Test that event IS dispatched when price changes to null
     */
    public function testEventDispatchedWhenPriceChangesToNull(): void
    {
        $vehicle = Vehicle::factory()->create(['price' => 10000.00]);
        $originalPrice = $vehicle->price;

        $vehicle->price = null;
        $vehicle->save();

        Event::fake([VehiclePriceChangedDuringStockSync::class]);

        $this->dispatchPriceChangeEvent($vehicle, 'test-provider', $originalPrice);

        Event::assertDispatched(VehiclePriceChangedDuringStockSync::class);
    }

    /**
     * Helper to dispatch price change event using the trait logic
     */
    private function dispatchPriceChangeEvent(Vehicle $vehicle, string $provider, mixed $originalPrice = null): void
    {
        // Use anonymous class to access the protected trait method
        $class = new class {
            use StockSyncTraits;

            protected function getProviderName(): string
            {
                return 'test-provider';
            }

            public function triggerPriceChangeEvents(
                Vehicle $vehicle,
                string $provider,
                mixed $originalPrice = null
            ): void {
                $this->priceChangeEvents($vehicle, $provider, $originalPrice);
            }
        };

        $class->triggerPriceChangeEvents($vehicle, $provider, $originalPrice);
    }
}
