<?php

namespace Tests\Tenant;

use App\Events\NewVehicleImported;
use App\Events\VehicleUpdatedFromImport;
use App\Imports\AutoGrabToVehicleImport;
use App\Services\AutoGrab\AutoGrabFeedImporter;
use App\Services\AutoGrab\AutoGrabFeedRowMapper;
use Illuminate\Support\Facades\Event;
use Mtc\MercuryDataModels\Vehicle;
use Tests\TenantTestCase;

class AutoGrabFeedImportTest extends TenantTestCase
{
    /**
     * A row shaped like the 26-column AutoGrab feed schema (no images / transmission / listing
     * sources / tag_ids — the file omits those).
     */
    private function row(array $overrides = []): array
    {
        return array_merge([
            'id' => 'ag-1',
            'vehicle_id' => 100,
            'rego' => 'AB12CDE',
            'vin' => 'SALVIN000000000001',
            'vehicle_title' => 'Land Rover Defender 110',
            'make' => 'Land Rover',
            'model' => 'Defender',
            'badge' => 'HSE',
            'series' => 'L663',
            'year' => 2020,
            'release_year' => 2020,
            'color' => 'Santorini Black',
            'fuel_type' => 'Diesel',
            'body_type' => 'SUV',
            'capacity_cc' => 2999,
            'num_seats' => 5,
            'num_doors' => 5,
            'power_kw' => 147,
            'vehicle_rrp' => 60000,
            'kms' => 25000,
            'price' => 45000,
            'postcode' => 'AB1 2CD',
            'contact_name' => 'Example Dealer',
            'updated_at' => '2026-07-01 06:00:00',
            'listed_at' => '2026-06-01 06:00:00',
            'removed_at' => null,
        ], $overrides);
    }

    public function testRowMapperSuppliesSafeDefaultsForOmittedColumns(): void
    {
        $lead = new AutoGrabFeedRowMapper()->toLead(['id' => 'ag-9', 'rego' => 'AB12CDE']);

        // tag_ids MUST default to [] — AutoGrabToVehicleImport::add() runs in_array() on it.
        $this->assertSame([], $lead['tag_ids']);
        $this->assertArrayHasKey('seller_type', $lead);
        $this->assertNull($lead['seller_type']);
        $this->assertSame([], $lead['listing_sources']);
        // Original columns are preserved.
        $this->assertSame('ag-9', $lead['id']);
        $this->assertSame('AB12CDE', $lead['rego']);
    }

    public function testImportBatchCreatesVehicleWithoutImages(): void
    {
        Event::fake([NewVehicleImported::class, VehicleUpdatedFromImport::class]);

        $stats = new AutoGrabFeedImporter()->importBatch([$this->row()]);

        $this->assertSame(1, $stats['processed']);
        $this->assertSame(0, $stats['failed']);

        $vehicle = Vehicle::query()
            ->where('stock_provider', 'autograb')
            ->where('uuid', 'ag-1')
            ->first();

        $this->assertNotNull($vehicle);
        // The Vehicle model normalises UK plates (e.g. "AB12 CDE"); compare condensed.
        $this->assertSame('AB12CDE', str_replace(' ', '', (string) $vehicle->registration_number));
        $this->assertEquals(45000, $vehicle->price);
        $this->assertTrue((bool) $vehicle->was_recently_synced);
        // The feed carries no imagery, so no media uses are created.
        $this->assertSame(0, $vehicle->mediaUses()->count());
    }

    public function testUnparseablePlateFallsBackToManufactureYearAndStaysPublished(): void
    {
        Event::fake([NewVehicleImported::class, VehicleUpdatedFromImport::class]);

        // Cherished prefix-format plate: the plate-based date finder returns null for it.
        $stats = new AutoGrabFeedImporter()->importBatch([
            $this->row(['rego' => 'Y31NAB', 'vin' => null, 'year' => 2018, 'release_year' => 2018]),
        ]);

        $this->assertSame(1, $stats['processed']);

        $vehicle = Vehicle::query()->where('uuid', 'ag-1')->first();

        $this->assertNotNull($vehicle);
        $this->assertSame('2018-01-01', $vehicle->first_registration_date?->format('Y-m-d'));
        $this->assertTrue((bool) $vehicle->is_published);
    }

    public function testUnparseablePlateWithoutYearImportsUnpublished(): void
    {
        Event::fake([NewVehicleImported::class, VehicleUpdatedFromImport::class]);

        $stats = new AutoGrabFeedImporter()->importBatch([
            $this->row(['rego' => 'Y31NAB', 'vin' => null, 'year' => null, 'release_year' => null]),
        ]);

        $this->assertSame(1, $stats['processed']);

        $vehicle = Vehicle::query()->where('uuid', 'ag-1')->first();

        $this->assertNotNull($vehicle);
        $this->assertNull($vehicle->first_registration_date);
        $this->assertFalse((bool) $vehicle->is_published);
    }

    public function testRepublishCommandHealsVehiclesSuppressedByUnreadablePlate(): void
    {
        Event::fake();

        // Suppressed by the bug: no reg date, but the stored feed payload carries the year.
        $suppressed = Vehicle::factory()->create([
            'stock_provider' => 'autograb',
            'uuid' => 'heal-me',
            'registration_number' => 'Y31NAB',
            'is_published' => false,
            'first_registration_date' => null,
            'data' => $this->row(['rego' => 'Y31NAB', 'year' => 2018]),
        ]);
        // No plate date and no year anywhere -> must stay untouched.
        $undatable = Vehicle::factory()->create([
            'stock_provider' => 'autograb',
            'uuid' => 'no-date',
            'registration_number' => 'Y31NAB2',
            'is_published' => false,
            'first_registration_date' => null,
            'data' => $this->row(['rego' => 'Y31NAB2', 'year' => null, 'release_year' => null]),
        ]);
        // Already dated and unpublished (e.g. by an admin) -> out of scope.
        $adminHidden = Vehicle::factory()->create([
            'stock_provider' => 'autograb',
            'uuid' => 'admin-hidden',
            'is_published' => false,
            'first_registration_date' => '2020-03-01',
        ]);
        // Another provider -> untouched even when it matches the shape.
        $otherProvider = Vehicle::factory()->create([
            'stock_provider' => 'motors',
            'uuid' => 'other-provider',
            'is_published' => false,
            'first_registration_date' => null,
        ]);

        $this->artisan('autograb:republish-missing-reg-date')->assertSuccessful();

        $this->assertTrue((bool) $suppressed->fresh()->is_published);
        $this->assertSame('2018-01-01', $suppressed->fresh()->first_registration_date?->format('Y-m-d'));

        $this->assertFalse((bool) $undatable->fresh()->is_published);
        $this->assertNull($undatable->fresh()->first_registration_date);

        $this->assertFalse((bool) $adminHidden->fresh()->is_published);
        $this->assertFalse((bool) $otherProvider->fresh()->is_published);
    }

    public function testRepublishCommandDryRunChangesNothing(): void
    {
        Event::fake();

        $suppressed = Vehicle::factory()->create([
            'stock_provider' => 'autograb',
            'uuid' => 'heal-me',
            'registration_number' => 'Y31NAB',
            'is_published' => false,
            'first_registration_date' => null,
            'data' => $this->row(['rego' => 'Y31NAB', 'year' => 2018]),
        ]);

        $this->artisan('autograb:republish-missing-reg-date', ['--dry-run' => true])->assertSuccessful();

        $this->assertFalse((bool) $suppressed->fresh()->is_published);
        $this->assertNull($suppressed->fresh()->first_registration_date);
    }

    public function testImportBatchUpdatesExistingVehicleAndClearsPendingFlag(): void
    {
        Event::fake([NewVehicleImported::class, VehicleUpdatedFromImport::class]);

        // Simulate the provider having marked this in-feed vehicle pending before dispatch.
        Vehicle::factory()->create([
            'stock_provider' => 'autograb',
            'uuid' => 'ag-1',
            'registration_number' => 'AB12CDE',
            'price' => 45000,
            'was_recently_synced' => false,
            'pending_stock_sync' => 1,
        ]);

        $stats = new AutoGrabFeedImporter()->importBatch([$this->row(['price' => 42000])]);

        $this->assertSame(1, $stats['processed']);
        $this->assertSame(1, Vehicle::query()->where('uuid', 'ag-1')->count());

        $vehicle = Vehicle::query()->where('uuid', 'ag-1')->first();
        $this->assertEquals(42000, $vehicle->price);
        $this->assertTrue((bool) $vehicle->was_recently_synced);
        $this->assertNull($vehicle->pending_stock_sync);
    }

    public function testDeactivateNotRecentlySyncedRemovesOnlyUnsyncedUnpendingVehicles(): void
    {
        Event::fake();

        // Synced this run -> kept.
        $synced = Vehicle::factory()->create([
            'stock_provider' => 'autograb',
            'uuid' => 'keep-synced',
            'was_recently_synced' => true,
            'pending_stock_sync' => null,
        ]);
        // Absent from the snapshot -> removed.
        $absent = Vehicle::factory()->create([
            'stock_provider' => 'autograb',
            'uuid' => 'drop-absent',
            'was_recently_synced' => false,
            'pending_stock_sync' => null,
        ]);
        // Still queued for an async batch -> protected.
        $pending = Vehicle::factory()->create([
            'stock_provider' => 'autograb',
            'uuid' => 'keep-pending',
            'was_recently_synced' => false,
            'pending_stock_sync' => 1,
        ]);
        // A different provider -> untouched.
        $other = Vehicle::factory()->create([
            'stock_provider' => 'motors',
            'uuid' => 'other-provider',
            'was_recently_synced' => false,
            'pending_stock_sync' => null,
        ]);

        $removed = new AutoGrabToVehicleImport()->deactivateNotRecentlySynced();

        $this->assertSame(1, $removed);
        $this->assertNotNull(Vehicle::query()->whereKey($synced->id)->first());
        $this->assertNull(Vehicle::query()->whereKey($absent->id)->first());
        $this->assertNotNull(Vehicle::withTrashed()->whereKey($absent->id)->first());
        $this->assertNotNull(Vehicle::query()->whereKey($pending->id)->first());
        $this->assertNotNull(Vehicle::query()->whereKey($other->id)->first());
    }

    public function testPriceIncludingGovtChargesIsStoredAsIsAndFlaggedVatApplicable(): void
    {
        Event::fake([NewVehicleImported::class, VehicleUpdatedFromImport::class]);

        // v2 feed: price_includes_govt_charges = true, so VAT is already in the price.
        $stats = new AutoGrabFeedImporter()->importBatch([
            $this->row([
                'price' => 45000,
                'price_before_govt_charges' => 37500,
                'price_includes_govt_charges' => true,
            ]),
        ]);

        $this->assertSame(1, $stats['processed']);

        $vehicle = Vehicle::query()->where('uuid', 'ag-1')->first();
        $this->assertNotNull($vehicle);
        // Price already carries VAT -> stored unchanged.
        $this->assertEquals(45000, $vehicle->price);
        $this->assertTrue((bool) $vehicle->is_vat_applicable);
    }

    public function testPriceExcludingGovtChargesIsGrossedUpByVatRate(): void
    {
        Event::fake([NewVehicleImported::class, VehicleUpdatedFromImport::class]);

        // v2 feed: price_includes_govt_charges = false means the listing arrived without VAT (the
        // under-pricing case). The ex-charges base is grossed up 20% so it is not published cheap.
        $stats = new AutoGrabFeedImporter()->importBatch([
            $this->row([
                'price' => 37500,
                'price_before_govt_charges' => 37500,
                'price_includes_govt_charges' => false,
            ]),
        ]);

        $this->assertSame(1, $stats['processed']);

        $vehicle = Vehicle::query()->where('uuid', 'ag-1')->first();
        $this->assertNotNull($vehicle);
        $this->assertEquals(45000, $vehicle->price); // 37500 * 1.20
        $this->assertTrue((bool) $vehicle->is_vat_applicable);
    }

    public function testFallsBackToPriceGapWhenGovtChargeFlagAbsent(): void
    {
        Event::fake([NewVehicleImported::class, VehicleUpdatedFromImport::class]);

        // No explicit flag: a positive gap (price_before_govt_charges < price) means VAT is
        // already included, so the price is kept as-is.
        $stats = new AutoGrabFeedImporter()->importBatch([
            $this->row(['price' => 45000, 'price_before_govt_charges' => 37500]),
        ]);

        $this->assertSame(1, $stats['processed']);

        $vehicle = Vehicle::query()->where('uuid', 'ag-1')->first();
        $this->assertNotNull($vehicle);
        $this->assertEquals(45000, $vehicle->price);
        $this->assertTrue((bool) $vehicle->is_vat_applicable);
    }

    public function testLegacyRowWithoutGovtChargeColumnsKeepsPriceAndDoesNotForceVatFlag(): void
    {
        Event::fake([NewVehicleImported::class, VehicleUpdatedFromImport::class]);

        // Existing vehicle explicitly marked not VAT applicable; a v1 row (none of the
        // govt-charges columns) must neither gross up the price nor override that flag.
        Vehicle::factory()->create([
            'stock_provider' => 'autograb',
            'uuid' => 'ag-1',
            'registration_number' => 'AB12CDE',
            'price' => 45000,
            'is_vat_applicable' => false,
            'was_recently_synced' => false,
        ]);

        $stats = new AutoGrabFeedImporter()->importBatch([$this->row(['price' => 45000])]);

        $this->assertSame(1, $stats['processed']);

        $vehicle = Vehicle::query()->where('uuid', 'ag-1')->first();
        $this->assertEquals(45000, $vehicle->price);
        $this->assertFalse((bool) $vehicle->is_vat_applicable);
    }
}
