<?php

namespace Tests\Tenant;

use App\Events\LoadDealershipDataFields;
use App\Events\NewVehicleImported;
use App\Events\StockSyncFinished;
use App\Events\VehiclePriceChangedDuringStockSync;
use App\Events\VehicleUpdatedFromImport;
use App\Facades\Settings;
use App\Jobs\ImportDealerKitVehicleBatch;
use App\Jobs\ImportImageUrlList;
use App\Modules\Stock\DealerKit;
use App\Services\DealerKitApi;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Queue;
use Mtc\MercuryDataModels\Dealership;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleSpecType;
use Mtc\MercuryDataModels\VehicleStandardEquipment;
use Mtc\MercuryDataModels\VehicleTechnicalData;
use Mtc\MercuryDataModels\VehicleType;
use Tests\TenantTestCase;

class DealerKitTest extends TenantTestCase
{
    private function makeSetting(string $key, mixed $value, string $type = 'boolean'): void
    {
        Settings::make([
            'tab' => 'foo',
            'section' => 'foo',
            'group' => 'foo',
            'name' => 'foo',
            'config_key' => $key,
            'value' => $value,
            'type' => $type,
        ]);
    }

    private function setupDefaults(): void
    {
        $this->makeSetting('stock-dealerkit-enabled', true);
        $this->makeSetting('stock-dealerkit-api-secret', 'test-secret', 'string');
        $this->makeSetting('stock-dealerkit-use-live-api', false);
    }

    private function samplePayload(array $overrides = []): array
    {
        return array_replace_recursive([
            'id' => 'DK-001',
            'vehicle' => [
                'vin' => 'WVWZZZ1JZXW000001',
                'registration' => 'AB12CDE',
                'type' => 'car',
                'manufacturer' => 'Ford',
                'model' => 'Focus',
                'derivative' => '1.0 EcoBoost Zetec',
                'trim' => 'Zetec',
                'body_type' => 'Hatchback',
                'engine_size' => 999,
                'fuel_type' => 'Petrol',
                'transmission_type' => 'Manual',
                'colour' => 'Blue',
                'number_of_doors' => 5,
                'number_of_seats' => 5,
                'mileage' => 25000,
                'mpg_combined' => 55,
                'co2' => 120,
                'bhp' => 125,
                'wheelbase_mm' => 2648,
                'year' => 2022,
                'registration_date' => '2022-03-15',
                'condition' => 'used',
                'cap_id' => 12345,
            ],
            'prices' => [
                'cash' => ['amount' => 14995.00],
                'advertised' => ['amount' => 14995.00],
            ],
            'advertising' => [
                'attention_grabber' => 'Great example',
                'comments' => 'Well looked after',
            ],
            'media' => [
                'cover_image' => ['id' => 'c1', 'url' => 'https://img.dealerkit.test/cover.jpg'],
                'images' => [
                    ['id' => 'i1', 'url' => 'https://img.dealerkit.test/1.jpg'],
                    ['id' => 'i2', 'url' => 'https://img.dealerkit.test/2.jpg'],
                ],
            ],
            'status' => 'Available',
        ], $overrides);
    }

    public function testEnabledReflectsStockToggle()
    {
        $this->makeSetting('stock-dealerkit-enabled', false);
        $this->assertFalse(app(DealerKit::class)->enabled());

        Settings::update('stock-dealerkit-enabled', true);
        $this->assertTrue(app(DealerKit::class)->enabled());
    }

    public function testName()
    {
        $this->assertEquals('DealerKit', app(DealerKit::class)->name());
    }

    public function testDealershipAdditionalDataFields()
    {
        $fields = app(DealerKit::class)->dealershipAdditionalDataFields();
        $this->assertArrayHasKey('dealerkit-dealer-id', $fields);
        $this->assertEquals('text', $fields['dealerkit-dealer-id']['type']);
    }

    public function testDealerIdFieldExposedViaLoadEventWhenStockEnabled()
    {
        $this->setupDefaults();
        $this->makeSetting('dealerkit-crm-enabled', false);

        $fields = $this->loadDealershipDataFields();

        $this->assertArrayHasKey('dealerkit-dealer-id', $fields);
    }

    public function testDealerIdFieldExposedViaLoadEventWhenOnlyCrmEnabled()
    {
        $this->makeSetting('stock-dealerkit-enabled', false);
        $this->makeSetting('dealerkit-crm-enabled', true);

        $fields = $this->loadDealershipDataFields();

        $this->assertArrayHasKey('dealerkit-dealer-id', $fields);
    }

    public function testDealerIdFieldHiddenViaLoadEventWhenBothDisabled()
    {
        $this->makeSetting('stock-dealerkit-enabled', false);
        $this->makeSetting('dealerkit-crm-enabled', false);

        $fields = $this->loadDealershipDataFields();

        $this->assertArrayNotHasKey('dealerkit-dealer-id', $fields);
    }

    /**
     * Collect dealership data fields the way DealershipViewResource does, so the
     * listener registration (not just the provider method) is exercised.
     */
    private function loadDealershipDataFields(): array
    {
        return collect(event(new LoadDealershipDataFields()))
            ->filter()
            ->mapWithKeys(fn ($field) => $field)
            ->all();
    }

    public function testApiEndpointSwitchesBetweenLiveAndTest()
    {
        $this->setupDefaults();

        $api = app(DealerKitApi::class);
        $this->assertStringStartsWith('https://api.dealerkit.test/', $api->endpoint('/integrators/stock'));

        Settings::update('stock-dealerkit-use-live-api', true);
        $api = app(DealerKitApi::class);
        $this->assertStringStartsWith('https://api.dealerkit.uk/', $api->endpoint('/integrators/stock'));
    }

    public function testApiSendsBearerTokenAndQueryParams()
    {
        $this->setupDefaults();
        Http::fake([
            '*/integrators/stock*' => Http::response([
                'data' => [],
                'meta' => ['current_page' => 1, 'last_page' => 1],
            ]),
        ]);

        app(DealerKitApi::class)->getStockPage('DEALER-42', 1, 25);

        Http::assertSent(function ($request) {
            return $request->hasHeader('Authorization', 'Bearer test-secret')
                && str_contains($request->url(), 'dealer_id=DEALER-42')
                && str_contains($request->url(), 'page=1')
                && str_contains($request->url(), 'per_page=25');
        });
    }

    public function testRunScheduledImportSkipsWhenNoDealershipsConfigured()
    {
        $this->setupDefaults();
        Queue::fake();
        Http::fake();

        Dealership::factory()->create(['data' => []]);

        app(DealerKit::class)->runScheduledImport();

        Queue::assertNothingPushed();
        Http::assertNothingSent();
    }

    public function testRunScheduledImportDispatchesBatchPerPage()
    {
        $this->setupDefaults();
        Queue::fake();

        Dealership::factory()->create(['data' => ['dealerkit-dealer-id' => 'DK-DEAL-1']]);

        Http::fake([
            '*/integrators/stock*' => Http::sequence()
                ->push([
                    'data' => [$this->samplePayload(['id' => 'P1-V1'])],
                    'meta' => ['current_page' => 1, 'last_page' => 2],
                ])
                ->push([
                    'data' => [$this->samplePayload(['id' => 'P2-V1'])],
                    'meta' => ['current_page' => 2, 'last_page' => 2],
                ]),
        ]);

        app(DealerKit::class)->runScheduledImport();

        Queue::assertPushed(ImportDealerKitVehicleBatch::class, 2);
    }

    public function testRunScheduledImportFiresStockSyncFinishedEvent()
    {
        $this->setupDefaults();
        Queue::fake();
        Event::fake([StockSyncFinished::class]);

        Dealership::factory()->create(['data' => ['dealerkit-dealer-id' => 'DK-DEAL-1']]);
        Http::fake([
            '*/integrators/stock*' => Http::response([
                'data' => [],
                'meta' => ['current_page' => 1, 'last_page' => 1],
            ]),
        ]);

        app(DealerKit::class)->runScheduledImport();

        Event::assertDispatched(
            StockSyncFinished::class,
            fn (StockSyncFinished $event) => $event->provider === 'dealerkit'
        );
    }

    public function testRunScheduledImportRemovesVehiclesMissingFromFeedForThisProviderOnly()
    {
        $this->setupDefaults();
        Queue::fake();

        Dealership::factory()->create(['data' => ['dealerkit-dealer-id' => 'DK-DEAL-1']]);

        // Existing DealerKit stock: A and B still in the feed, C dropped out.
        Vehicle::factory()->create(['stock_provider' => 'dealerkit', 'uuid' => 'A']);
        Vehicle::factory()->create(['stock_provider' => 'dealerkit', 'uuid' => 'B']);
        Vehicle::factory()->create(['stock_provider' => 'dealerkit', 'uuid' => 'C']);
        // A different provider's vehicle absent from the feed must be untouched.
        Vehicle::factory()->create(['stock_provider' => 'auto-trader', 'uuid' => 'AT-1']);

        Http::fake([
            '*/integrators/stock*' => Http::response([
                'data' => [$this->samplePayload(['id' => 'A']), $this->samplePayload(['id' => 'B'])],
                'meta' => ['current_page' => 1, 'last_page' => 1],
            ]),
        ]);

        app(DealerKit::class)->runScheduledImport();

        // In-feed vehicles survive even though the (faked) batch jobs never ran,
        // proving the synchronous pending marker protects them.
        $this->assertTrue(Vehicle::query()->where('uuid', 'A')->exists());
        $this->assertTrue(Vehicle::query()->where('uuid', 'B')->exists());

        // The dropped vehicle is soft deleted, not hard deleted.
        $this->assertFalse(Vehicle::query()->where('uuid', 'C')->exists());
        $this->assertTrue(Vehicle::withTrashed()->where('uuid', 'C')->firstOrFail()->trashed());

        // Other providers are never touched.
        $this->assertTrue(Vehicle::query()->where('uuid', 'AT-1')->exists());
    }

    public function testRunScheduledImportRemovesDroppedVehiclesWhenBatchesRunBeforeRemoval()
    {
        $this->setupDefaults();

        // Run the batch jobs inline so they execute *before* removeOld(), the
        // same ordering that can occur on live when a queue worker processes a
        // batch (and clears the per-vehicle sync flags) while the import is
        // still running. Removal must not depend on that timing. Faking events
        // keeps the inline batch from triggering downstream import listeners.
        config(['queue.default' => 'sync']);
        Event::fake();

        Dealership::factory()->create(['data' => ['dealerkit-dealer-id' => 'DK-DEAL-1']]);

        // A and B are in the feed, C has dropped out.
        Vehicle::factory()->create(['stock_provider' => 'dealerkit', 'uuid' => 'A']);
        Vehicle::factory()->create(['stock_provider' => 'dealerkit', 'uuid' => 'B']);
        Vehicle::factory()->create(['stock_provider' => 'dealerkit', 'uuid' => 'C']);

        Http::fake([
            '*/integrators/stock*' => Http::response([
                'data' => [$this->samplePayload(['id' => 'A']), $this->samplePayload(['id' => 'B'])],
                'meta' => ['current_page' => 1, 'last_page' => 1],
            ]),
        ]);

        app(DealerKit::class)->runScheduledImport();

        $this->assertTrue(Vehicle::query()->where('uuid', 'A')->exists());
        $this->assertTrue(Vehicle::query()->where('uuid', 'B')->exists());

        // C is removed even though the batches ran inline and cleared the
        // per-vehicle sync flags before removeOld() executed.
        $this->assertFalse(Vehicle::query()->where('uuid', 'C')->exists());
        $this->assertTrue(Vehicle::withTrashed()->where('uuid', 'C')->firstOrFail()->trashed());
    }

    public function testRunScheduledImportSkipsRemovalWhenFeedFallsBelowMargin()
    {
        $this->setupDefaults();
        Queue::fake();

        Dealership::factory()->create(['data' => ['dealerkit-dealer-id' => 'DK-DEAL-1']]);

        // Only 1 of 3 existing vehicles returns in the feed (33% < default 0.5),
        // so removals are suppressed to avoid wiping stock on a bad/partial feed.
        Vehicle::factory()->create(['stock_provider' => 'dealerkit', 'uuid' => 'A']);
        Vehicle::factory()->create(['stock_provider' => 'dealerkit', 'uuid' => 'B']);
        Vehicle::factory()->create(['stock_provider' => 'dealerkit', 'uuid' => 'C']);

        Http::fake([
            '*/integrators/stock*' => Http::response([
                'data' => [$this->samplePayload(['id' => 'A'])],
                'meta' => ['current_page' => 1, 'last_page' => 1],
            ]),
        ]);

        app(DealerKit::class)->runScheduledImport();

        $this->assertTrue(Vehicle::query()->where('uuid', 'A')->exists());
        $this->assertTrue(Vehicle::query()->where('uuid', 'B')->exists());
        $this->assertTrue(Vehicle::query()->where('uuid', 'C')->exists());
    }

    public function testRunScheduledImportTracksCreatedUpdatedAndProcessedCounts()
    {
        $this->setupDefaults();
        Queue::fake();

        Dealership::factory()->create(['data' => ['dealerkit-dealer-id' => 'DK-DEAL-1']]);

        // 'A' already exists (an update); 'B' is new (a create).
        Vehicle::factory()->create(['stock_provider' => 'dealerkit', 'uuid' => 'A']);

        Http::fake([
            '*/integrators/stock*' => Http::response([
                'data' => [$this->samplePayload(['id' => 'A']), $this->samplePayload(['id' => 'B'])],
                'meta' => ['current_page' => 1, 'last_page' => 1],
            ]),
        ]);

        $dealerKit = app(DealerKit::class);
        $dealerKit->runScheduledImport();

        $this->assertEquals(
            ['processed' => 2, 'created' => 1, 'updated' => 1, 'failed' => 0],
            $dealerKit->getImportStats(),
        );
    }

    public function testBatchJobCreatesVehicleWithMappedFields()
    {
        $this->setupDefaults();
        Event::fake([NewVehicleImported::class, VehicleUpdatedFromImport::class]);

        $dealership = Dealership::factory()->create(['data' => ['dealerkit-dealer-id' => 'DK-DEAL-1']]);

        $job = new ImportDealerKitVehicleBatch(collect([$this->samplePayload()]), $dealership->id);
        $job->handle();

        $vehicle = Vehicle::query()->where('uuid', 'DK-001')->firstOrFail();
        $this->assertEquals('DK-001', $vehicle->uuid);
        $this->assertEquals('dealerkit', $vehicle->stock_provider);
        $this->assertEquals('WVWZZZ1JZXW000001', $vehicle->vin);
        $this->assertEquals('AB12 CDE', $vehicle->registration_number);
        $this->assertEquals(12345, $vehicle->cap_id);
        $this->assertEquals(14995.00, (float) $vehicle->price);
        $this->assertEquals(25000, $vehicle->odometer_mi);
        $this->assertEquals(2022, $vehicle->manufacture_year);
        $this->assertEquals('Blue', $vehicle->colour);
        $this->assertEquals(VehicleType::CAR->value, $vehicle->type);
        $this->assertFalse($vehicle->is_new);
        $this->assertEquals('Ford Focus', $vehicle->title);
        $this->assertEquals($dealership->id, $vehicle->dealership_id);
        $this->assertTrue((bool) $vehicle->is_published, 'new vehicles default to published');

        Event::assertDispatched(NewVehicleImported::class);
        Event::assertNotDispatched(VehicleUpdatedFromImport::class);
    }

    public function testBatchJobDispatchesImageImportWhenImagesChange()
    {
        $this->setupDefaults();
        $this->makeSetting('stock-dealerkit-sync-images', true);
        Event::fake();
        Queue::fake();

        (new ImportDealerKitVehicleBatch(collect([$this->samplePayload()]), null))->handle();

        $vehicle = Vehicle::query()->where('uuid', 'DK-001')->firstOrFail();

        Queue::assertPushed(ImportImageUrlList::class, function (ImportImageUrlList $job) use ($vehicle) {
            $reflection = new \ReflectionObject($job);
            $images = $reflection->getProperty('images')->getValue($job);
            $owner = $reflection->getProperty('owner')->getValue($job);
            $config = $reflection->getProperty('config')->getValue($job);

            return $owner->is($vehicle)
                && $images->all() === [
                    'https://img.dealerkit.test/cover.jpg',
                    'https://img.dealerkit.test/1.jpg',
                    'https://img.dealerkit.test/2.jpg',
                ]
                && ($config['imageProvider'] ?? null) === 'dealerkit';
        });
    }

    public function testBatchJobSkipsImageImportWhenChecksumUnchanged()
    {
        $this->setupDefaults();
        $this->makeSetting('stock-dealerkit-sync-images', true);
        Event::fake();
        Queue::fake();

        $urls = [
            'https://img.dealerkit.test/cover.jpg',
            'https://img.dealerkit.test/1.jpg',
            'https://img.dealerkit.test/2.jpg',
        ];

        Vehicle::factory()->create([
            'stock_provider' => 'dealerkit',
            'uuid' => 'DK-001',
            'image_checksum' => dechex(crc32(json_encode(array_merge([count($urls)], $urls)))),
        ]);

        (new ImportDealerKitVehicleBatch(collect([$this->samplePayload()]), null))->handle();

        Queue::assertNotPushed(ImportImageUrlList::class);
    }

    public function testBatchJobDoesNotSyncImagesWhenSettingDisabled()
    {
        $this->setupDefaults();
        $this->makeSetting('stock-dealerkit-sync-images', false);
        Event::fake();
        Queue::fake();

        (new ImportDealerKitVehicleBatch(collect([$this->samplePayload()]), null))->handle();

        Queue::assertNotPushed(ImportImageUrlList::class);
    }

    public function testBatchJobMarksConditionNewVehicleAsIsNew()
    {
        $this->setupDefaults();
        Event::fake();

        $job = new ImportDealerKitVehicleBatch(
            collect([$this->samplePayload(['vehicle' => ['condition' => 'new']])]),
            null,
        );
        $job->handle();

        $vehicle = Vehicle::query()->where('uuid', 'DK-001')->firstOrFail();
        $this->assertEquals('DK-001', $vehicle->uuid);
        $this->assertTrue($vehicle->is_new);
    }

    public function testBatchJobMapsLcvTypes()
    {
        $this->setupDefaults();
        Event::fake();

        $job = new ImportDealerKitVehicleBatch(
            collect([
                $this->samplePayload(['id' => 'LCV-1', 'vehicle' => ['type' => 'lcv']]),
                $this->samplePayload(['id' => 'VAN-1', 'vehicle' => ['type' => 'van']]),
            ]),
            null,
        );
        $job->handle();

        $this->assertEquals(
            VehicleType::LCV->value,
            Vehicle::query()->where('uuid', 'LCV-1')->firstOrFail()->type,
            'Vehicle LCV-1 should map to LCV'
        );

        // 'van' is not a recognised type and falls back to CAR.
        $this->assertEquals(
            VehicleType::CAR->value,
            Vehicle::query()->where('uuid', 'VAN-1')->firstOrFail()->type,
            'Vehicle VAN-1 should fall back to CAR'
        );
    }

    public function testBatchJobMapsVatStatusToIsVatApplicable()
    {
        $this->setupDefaults();
        Event::fake();

        $job = new ImportDealerKitVehicleBatch(
            collect([
                $this->samplePayload([
                    'id' => 'VAT-INC',
                    'prices' => ['advertised' => ['vat_status' => 'inc-VAT']],
                ]),
                $this->samplePayload([
                    'id' => 'VAT-EX',
                    'prices' => ['advertised' => ['vat_status' => 'ex-VAT']],
                ]),
                $this->samplePayload([
                    'id' => 'VAT-NONE',
                    'prices' => ['advertised' => ['vat_status' => 'No VAT']],
                ]),
                $this->samplePayload(['id' => 'VAT-ABSENT']),
            ]),
            null,
        );
        $job->handle();

        $this->assertTrue((bool) Vehicle::query()->where('uuid', 'VAT-INC')->firstOrFail()->is_vat_applicable);
        $this->assertFalse((bool) Vehicle::query()->where('uuid', 'VAT-EX')->firstOrFail()->is_vat_applicable);
        $this->assertFalse((bool) Vehicle::query()->where('uuid', 'VAT-NONE')->firstOrFail()->is_vat_applicable);
        $this->assertTrue((bool) Vehicle::query()->where('uuid', 'VAT-ABSENT')->firstOrFail()->is_vat_applicable);
    }

    public function testBatchJobUpdatesExistingVehicleAndFiresUpdateEvent()
    {
        $this->setupDefaults();
        Event::fake([
            NewVehicleImported::class,
            VehicleUpdatedFromImport::class,
            VehiclePriceChangedDuringStockSync::class,
        ]);

        Vehicle::factory()->create([
            'stock_provider' => 'dealerkit',
            'uuid' => 'DK-001',
            'price' => 9999,
            'colour' => 'Red',
        ]);

        $job = new ImportDealerKitVehicleBatch(collect([$this->samplePayload()]), null);
        $job->handle();

        $vehicle = Vehicle::query()->where('uuid', 'DK-001')->firstOrFail();
        $this->assertEquals('DK-001', $vehicle->uuid);
        $this->assertEquals(14995.00, (float) $vehicle->price);
        $this->assertEquals('Blue', $vehicle->colour, 'overwrites existing fields on update');
        $this->assertTrue((bool) $vehicle->was_recently_synced);
        $this->assertEquals(9999.00, (float) $vehicle->previous_price, 'tracks the prior price we held');

        Event::assertDispatched(VehicleUpdatedFromImport::class);
        Event::assertNotDispatched(NewVehicleImported::class);
        Event::assertDispatched(VehiclePriceChangedDuringStockSync::class);
    }

    public function testBatchJobPersistsStandardAndOptionalEquipment()
    {
        $this->setupDefaults();
        Event::fake();

        $payload = $this->samplePayload([
            'vehicle' => [
                'specifications' => [
                    'standard' => [
                        'items' => [
                            [
                                'label' => 'Air Conditioning',
                                'generic_label' => 'air-con',
                                'description' => 'Manual air conditioning',
                                'group' => 'Comfort',
                                'price' => '0.00',
                                'factory_fitted' => true,
                            ],
                        ],
                    ],
                    'options' => [
                        'items' => [
                            [
                                'label' => 'Panoramic Roof',
                                'generic_label' => 'pano-roof',
                                'description' => 'Glass panoramic roof',
                                'group' => 'Exterior',
                                'price' => '950.00',
                                'factory_fitted' => true,
                            ],
                        ],
                    ],
                ],
            ],
        ]);

        $job = new ImportDealerKitVehicleBatch(collect([$payload]), null);
        $job->handle();

        $vehicle = Vehicle::query()->where('uuid', 'DK-001')->firstOrFail();

        $standard = VehicleStandardEquipment::query()
            ->where('vehicle_id', $vehicle->id)
            ->where('type', VehicleSpecType::STANDARD_EQUIPMENT->value)
            ->get();
        $this->assertCount(1, $standard);
        $this->assertEquals('Air Conditioning', $standard->first()->description);
        $this->assertEquals('air-con', $standard->first()->code);
        $this->assertEquals('Comfort', $standard->first()->category);
        $this->assertEquals(0.00, (float) $standard->first()->price);

        $optional = VehicleStandardEquipment::query()
            ->where('vehicle_id', $vehicle->id)
            ->where('type', VehicleSpecType::OPTIONAL_EQUIPMENT->value)
            ->get();
        $this->assertCount(1, $optional);
        $this->assertEquals('Panoramic Roof', $optional->first()->description);
        $this->assertEquals(950.00, (float) $optional->first()->price);
    }

    public function testBatchJobPersistsTechnicalDataUsingLabelAsKeyAndDescriptionAsValue()
    {
        $this->setupDefaults();
        Event::fake();

        $payload = $this->samplePayload([
            'vehicle' => [
                'specifications' => [
                    'technical' => [
                        'items' => [
                            [
                                'label' => 'Engine Power',
                                'generic_label' => 'engine-power',
                                'description' => '125 BHP',
                                'group' => 'Engine',
                                'price' => '0.00',
                                'factory_fitted' => true,
                            ],
                            [
                                'label' => 'Fuel Tank Capacity',
                                'generic_label' => 'fuel-tank',
                                'description' => '50 litres',
                                'group' => 'Engine',
                                'price' => '0.00',
                                'factory_fitted' => true,
                            ],
                        ],
                    ],
                ],
            ],
        ]);

        $job = new ImportDealerKitVehicleBatch(collect([$payload]), null);
        $job->handle();

        $vehicle = Vehicle::query()->where('uuid', 'DK-001')->firstOrFail();

        $tech = VehicleTechnicalData::query()
            ->where('vehicle_id', $vehicle->id)
            ->orderBy('description')
            ->get();
        $this->assertCount(2, $tech);
        $this->assertEquals('Engine Power', $tech->first()->description);
        $this->assertEquals('125 BHP', $tech->first()->value);
        $this->assertEquals('engine-power', $tech->first()->code);
        $this->assertEquals('Engine', $tech->first()->category);
    }

    public function testBatchJobSkipsSpecificationItemsMissingLabel()
    {
        $this->setupDefaults();
        Event::fake();

        $payload = $this->samplePayload([
            'vehicle' => [
                'specifications' => [
                    'standard' => [
                        'items' => [
                            ['label' => '', 'description' => 'No label'],
                            ['description' => 'Also no label'],
                        ],
                    ],
                    'technical' => [
                        'items' => [
                            ['label' => 'No value', 'description' => ''],
                            ['label' => 'Also no value'],
                        ],
                    ],
                ],
            ],
        ]);

        $job = new ImportDealerKitVehicleBatch(collect([$payload]), null);
        $job->handle();

        $vehicle = Vehicle::query()->where('uuid', 'DK-001')->firstOrFail();
        $this->assertEquals(0, VehicleStandardEquipment::query()->where('vehicle_id', $vehicle->id)->count());
        $this->assertEquals(0, VehicleTechnicalData::query()->where('vehicle_id', $vehicle->id)->count());
    }

    public function testBatchJobUpdatesExistingSpecificationRowsOnReimport()
    {
        $this->setupDefaults();
        Event::fake();

        $first = $this->samplePayload([
            'vehicle' => [
                'specifications' => [
                    'standard' => [
                        'items' => [
                            ['label' => 'Air Conditioning', 'group' => 'Comfort', 'price' => '0.00'],
                        ],
                    ],
                    'technical' => [
                        'items' => [
                            ['label' => 'Engine Power', 'description' => '120 BHP', 'group' => 'Engine'],
                        ],
                    ],
                ],
            ],
        ]);
        (new ImportDealerKitVehicleBatch(collect([$first]), null))->handle();

        $second = $this->samplePayload([
            'vehicle' => [
                'specifications' => [
                    'standard' => [
                        'items' => [
                            ['label' => 'Air Conditioning', 'group' => 'Climate', 'price' => '0.00'],
                        ],
                    ],
                    'technical' => [
                        'items' => [
                            ['label' => 'Engine Power', 'description' => '125 BHP', 'group' => 'Engine'],
                        ],
                    ],
                ],
            ],
        ]);
        (new ImportDealerKitVehicleBatch(collect([$second]), null))->handle();

        $vehicle = Vehicle::query()->where('uuid', 'DK-001')->firstOrFail();
        $this->assertEquals(
            1,
            VehicleStandardEquipment::query()->where('vehicle_id', $vehicle->id)->count(),
        );
        $this->assertEquals(
            'Climate',
            VehicleStandardEquipment::query()->where('vehicle_id', $vehicle->id)->first()->category,
        );
        $this->assertEquals(
            1,
            VehicleTechnicalData::query()->where('vehicle_id', $vehicle->id)->count(),
        );
        $this->assertEquals(
            '125 BHP',
            VehicleTechnicalData::query()->where('vehicle_id', $vehicle->id)->first()->value,
        );
    }

    public function testBatchJobSkipsEntriesWithoutId()
    {
        $this->setupDefaults();
        Event::fake();

        $job = new ImportDealerKitVehicleBatch(
            collect([['vehicle' => ['registration' => 'XX00YYY']]]),
            null,
        );
        $job->handle();

        $this->assertEquals(0, Vehicle::query()->count());
    }
}
