<?php

namespace Tests\Feature;

use App\Events\NewVehicleImported;
use App\Events\StockSyncFinished;
use App\Events\VehicleUpdatedFromImport;
use App\Listeners\IndexVehicleInMeilisearch;
use App\Listeners\RemoveDeletedVehicleFromMeilisearch;
use App\Listeners\SyncVehiclesToMeilisearch;
use App\Services\MeilisearchService;
use Carbon\Carbon;
use Mockery\MockInterface;
use Mtc\MercuryDataModels\Vehicle;
use Tests\TenantTestCase;

class MeilisearchListenerTest extends TenantTestCase
{
    /**
     * Test that StockSyncFinished triggers full reindex for batch providers.
     */
    public function testStockSyncFinishedTriggersReindexForBatchProvider(): void
    {
        $this->mock(MeilisearchService::class, function (MockInterface $mock) {
            $mock->shouldReceive('isEnabled')->once()->andReturn(true);
            $mock->shouldReceive('syncAllVehicles')->once();
        });

        $listener = $this->app->make(SyncVehiclesToMeilisearch::class);
        $event = new StockSyncFinished('motordata');

        $listener->handle($event);
    }

    /**
     * Test that StockSyncFinished does NOT trigger reindex for AutoGrab provider.
     */
    public function testStockSyncFinishedSkipsAutograbProvider(): void
    {
        $this->mock(MeilisearchService::class, function (MockInterface $mock) {
            $mock->shouldReceive('isEnabled')->once()->andReturn(true);
            $mock->shouldNotReceive('syncAllVehicles');
        });

        $listener = $this->app->make(SyncVehiclesToMeilisearch::class);
        $event = new StockSyncFinished('autograb');

        $listener->handle($event);
    }

    /**
     * Test that StockSyncFinished does NOT trigger reindex for AutoTrader Hub provider.
     */
    public function testStockSyncFinishedSkipsAutoTraderHubProvider(): void
    {
        $this->mock(MeilisearchService::class, function (MockInterface $mock) {
            $mock->shouldReceive('isEnabled')->once()->andReturn(true);
            $mock->shouldNotReceive('syncAllVehicles');
        });

        $listener = $this->app->make(SyncVehiclesToMeilisearch::class);
        $event = new StockSyncFinished('auto-trader');

        $listener->handle($event);
    }

    /**
     * Test that StockSyncFinished does nothing when Meilisearch is disabled.
     */
    public function testStockSyncFinishedSkipsWhenMeilisearchDisabled(): void
    {
        $this->mock(MeilisearchService::class, function (MockInterface $mock) {
            $mock->shouldReceive('isEnabled')->once()->andReturn(false);
            $mock->shouldNotReceive('syncAllVehicles');
        });

        $listener = $this->app->make(SyncVehiclesToMeilisearch::class);
        $event = new StockSyncFinished('motordata');

        $listener->handle($event);
    }

    /**
     * Test that NewVehicleImported indexes vehicle for AutoGrab provider.
     */
    public function testNewVehicleImportedIndexesForAutograb(): void
    {
        $vehicle = Vehicle::factory()->create();

        $this->mock(MeilisearchService::class, function (MockInterface $mock) use ($vehicle) {
            $mock->shouldReceive('isEnabled')->once()->andReturn(true);
            $mock->shouldReceive('indexVehicle')
                ->once()
                ->with(\Mockery::on(fn ($v) => $v->id === $vehicle->id));
        });

        $listener = $this->app->make(IndexVehicleInMeilisearch::class);
        $event = new NewVehicleImported($vehicle, [], 'autograb');

        $listener->handle($event);
    }

    /**
     * Test that NewVehicleImported indexes vehicle for AutoTrader Hub provider.
     */
    public function testNewVehicleImportedIndexesForAutoTraderHub(): void
    {
        $vehicle = Vehicle::factory()->create();

        $this->mock(MeilisearchService::class, function (MockInterface $mock) use ($vehicle) {
            $mock->shouldReceive('isEnabled')->once()->andReturn(true);
            $mock->shouldReceive('indexVehicle')
                ->once()
                ->with(\Mockery::on(fn ($v) => $v->id === $vehicle->id));
        });

        $listener = $this->app->make(IndexVehicleInMeilisearch::class);
        $event = new NewVehicleImported($vehicle, [], 'auto-trader');

        $listener->handle($event);
    }

    /**
     * Test that NewVehicleImported does NOT index for batch providers.
     */
    public function testNewVehicleImportedSkipsBatchProviders(): void
    {
        $vehicle = Vehicle::factory()->create();

        $this->mock(MeilisearchService::class, function (MockInterface $mock) {
            $mock->shouldReceive('isEnabled')->once()->andReturn(true);
            $mock->shouldNotReceive('indexVehicle');
        });

        $listener = $this->app->make(IndexVehicleInMeilisearch::class);
        $event = new NewVehicleImported($vehicle, [], 'motordata');

        $listener->handle($event);
    }

    /**
     * Test that NewVehicleImported does nothing when Meilisearch is disabled.
     */
    public function testNewVehicleImportedSkipsWhenMeilisearchDisabled(): void
    {
        $vehicle = Vehicle::factory()->create();

        $this->mock(MeilisearchService::class, function (MockInterface $mock) {
            $mock->shouldReceive('isEnabled')->once()->andReturn(false);
            $mock->shouldNotReceive('indexVehicle');
        });

        $listener = $this->app->make(IndexVehicleInMeilisearch::class);
        $event = new NewVehicleImported($vehicle, [], 'autograb');

        $listener->handle($event);
    }

    /**
     * Test that VehicleUpdatedFromImport indexes vehicle for AutoGrab provider.
     */
    public function testVehicleUpdatedFromImportIndexesForAutograb(): void
    {
        $vehicle = Vehicle::factory()->create();

        $this->mock(MeilisearchService::class, function (MockInterface $mock) use ($vehicle) {
            $mock->shouldReceive('isEnabled')->once()->andReturn(true);
            $mock->shouldReceive('indexVehicle')
                ->once()
                ->with(\Mockery::on(fn ($v) => $v->id === $vehicle->id));
        });

        $listener = $this->app->make(IndexVehicleInMeilisearch::class);
        $event = new VehicleUpdatedFromImport($vehicle, [], 'autograb');

        $listener->handle($event);
    }

    /**
     * Test that VehicleUpdatedFromImport does NOT index for batch providers.
     */
    public function testVehicleUpdatedFromImportSkipsBatchProviders(): void
    {
        $vehicle = Vehicle::factory()->create();

        $this->mock(MeilisearchService::class, function (MockInterface $mock) {
            $mock->shouldReceive('isEnabled')->once()->andReturn(true);
            $mock->shouldNotReceive('indexVehicle');
        });

        $listener = $this->app->make(IndexVehicleInMeilisearch::class);
        $event = new VehicleUpdatedFromImport($vehicle, [], 'motordata');

        $listener->handle($event);
    }

    /**
     * Test that an import which soft-deletes a vehicle removes it from the index,
     * regardless of provider.
     */
    public function testVehicleUpdatedFromImportRemovesNewlyDeletedVehicle(): void
    {
        $vehicle = Vehicle::factory()->create();
        $vehicle->deleted_at = Carbon::now();
        // Mark deleted_at as changed without persisting (mirrors the import's fill+save).
        $vehicle->syncChanges();

        $this->mock(MeilisearchService::class, function (MockInterface $mock) use ($vehicle) {
            $mock->shouldReceive('isEnabled')->once()->andReturn(true);
            $mock->shouldReceive('deleteVehicle')->once()->with($vehicle->id);
        });

        $listener = $this->app->make(RemoveDeletedVehicleFromMeilisearch::class);
        $event = new VehicleUpdatedFromImport($vehicle, [], 'motordata');

        $listener->handle($event);
    }

    /**
     * Test that an import which does not change deleted_at does not remove the vehicle.
     */
    public function testVehicleUpdatedFromImportDoesNotRemoveWhenDeletedAtUnchanged(): void
    {
        $vehicle = Vehicle::factory()->create();

        $this->mock(MeilisearchService::class, function (MockInterface $mock) {
            $mock->shouldReceive('isEnabled')->once()->andReturn(true);
            $mock->shouldNotReceive('deleteVehicle');
        });

        $listener = $this->app->make(RemoveDeletedVehicleFromMeilisearch::class);
        $event = new VehicleUpdatedFromImport($vehicle, [], 'auto-trader');

        $listener->handle($event);
    }

    /**
     * Test that restoring a vehicle (deleted_at changed to null) does not trigger removal.
     */
    public function testVehicleUpdatedFromImportDoesNotRemoveOnRestore(): void
    {
        $vehicle = Vehicle::factory()->create(['deleted_at' => Carbon::now()]);
        $vehicle->deleted_at = null;
        // deleted_at changed, but to null (a restore) — must not trigger removal.
        $vehicle->syncChanges();

        $this->mock(MeilisearchService::class, function (MockInterface $mock) {
            $mock->shouldReceive('isEnabled')->once()->andReturn(true);
            $mock->shouldNotReceive('deleteVehicle');
        });

        $listener = $this->app->make(RemoveDeletedVehicleFromMeilisearch::class);
        $event = new VehicleUpdatedFromImport($vehicle, [], 'auto-trader');

        $listener->handle($event);
    }

    /**
     * Test that the removal listener does nothing when Meilisearch is disabled.
     */
    public function testRemoveDeletedVehicleSkipsWhenMeilisearchDisabled(): void
    {
        $vehicle = Vehicle::factory()->create();
        $vehicle->deleted_at = Carbon::now();

        $this->mock(MeilisearchService::class, function (MockInterface $mock) {
            $mock->shouldReceive('isEnabled')->once()->andReturn(false);
            $mock->shouldNotReceive('deleteVehicle');
        });

        $listener = $this->app->make(RemoveDeletedVehicleFromMeilisearch::class);
        $event = new VehicleUpdatedFromImport($vehicle, [], 'auto-trader');

        $listener->handle($event);
    }
}
