<?php

namespace Tests\Tenant;

use App\Facades\Settings;
use App\Jobs\ImportImagesFromUrlList;
use App\Modules\Stock\Pinewood;
use App\VehicleType;
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Queue;
use Mtc\MercuryDataModels\Dealership;
use Mtc\MercuryDataModels\Media;
use Mtc\MercuryDataModels\Vehicle;
use Tests\TenantTestCase;

class PinewoodTest 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 setupPinewoodDefaults(): void
    {
        $this->makeSetting('stock-pinewood-enabled', true);
        $this->makeSetting('stock-pinewood-sync-new-vehicles', true);
        $this->makeSetting('stock-pinewood-sync-used-vehicles', true);
        $this->makeSetting('stock-pinewood-sync-images', true);
        $this->makeSetting('stock-pinewood-sync-only-with-internet-price', false);
        $this->makeSetting('stock-pinewood-sync-with-price', false);
        $this->makeSetting('stock-pinewood-username', 'testuser', 'string');
        $this->makeSetting('stock-pinewood-password', 'testpass', 'string');
    }

    private function vehiclePayload(array $overrides = []): array
    {
        return array_merge([
            'Vehicle_UID' => 'VH-001',
            'RegistrationNumber' => 'AB12CDE',
            'RegistrationDate' => '2022-03-15',
            'Make' => ['Make' => 'Ford'],
            'Model' => ['Model' => 'Focus'],
            'Specification' => ['Specification' => 'Zetec 1.0T'],
            'Colour' => 'Blue',
            'FuelType' => ['FuelType' => 'Petrol'],
            'BodyStyle' => ['BodyStyle' => 'Hatchback'],
            'Transmission' => ['Transmission' => 'Manual'],
            'EngineSize' => 999,
            'InternetPrice' => 15995,
            'RetailPrice' => 16500,
            'IsNew' => false,
            'VehicleType' => '1',
            'VIN' => 'WF0XXXGCDX1234567',
            'Odometer' => 25000,
            'PreviousOwners' => 2,
            'CO2Emission' => 120,
            'Status' => 0,
            'Profiles' => [],
            'Images' => [],
            'Videos' => [],
            'StockNumber' => 'STK-001',
        ], $overrides);
    }

    public function testEnabled()
    {
        $this->makeSetting('stock-pinewood-enabled', false);
        $this->assertFalse((new Pinewood())->enabled());

        Settings::update('stock-pinewood-enabled', true);
        $this->assertTrue((new Pinewood())->enabled());
    }

    public function testName()
    {
        $this->assertEquals('Pinewood DMS', (new Pinewood())->name());
    }

    public function testDealershipAdditionalDataFields()
    {
        $fields = (new Pinewood())->dealershipAdditionalDataFields();
        $this->assertArrayHasKey('pinewood-location-id', $fields);
        $this->assertEquals('text', $fields['pinewood-location-id']['type']);
    }

    public function testImportCreatesVehicles()
    {
        Queue::fake();
        $this->setupPinewoodDefaults();

        Http::fake([
            'https://api.pinnacledms.net/*' => Http::response([
                $this->vehiclePayload(),
                $this->vehiclePayload([
                    'Vehicle_UID' => 'VH-002',
                    'RegistrationNumber' => 'XY34FGH',
                    'Make' => ['Make' => 'BMW'],
                    'Model' => ['Model' => '3 Series'],
                    'InternetPrice' => 29995,
                    'VehicleType' => '1',
                    'VIN' => 'WBA12345678901234',
                ]),
            ]),
        ]);

        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-1']]);
        (new Pinewood())->runScheduledImport();

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

        $vehicle = Vehicle::query()->where('uuid', 'VH-001')->first();
        $this->assertNotNull($vehicle);
        $this->assertEquals('pinewood', $vehicle->stock_provider);
        $this->assertEquals('AB12 CDE', $vehicle->registration_number);
        $this->assertEquals(15995, (int) $vehicle->price);
        $this->assertEquals(999, (int) $vehicle->engine_size_cc);
        $this->assertEquals('WF0XXXGCDX1234567', $vehicle->vin);
        $this->assertEquals(25000, (int) $vehicle->odometer_mi);
        $this->assertEquals('Blue', $vehicle->colour);
        $this->assertFalse((bool) $vehicle->is_new);
        $this->assertEquals(VehicleType::CAR->value, $vehicle->type);
    }

    public function testImportSendsCorrectApiRequest()
    {
        Queue::fake();
        $this->setupPinewoodDefaults();

        Http::fake([
            'https://api.pinnacledms.net/*' => Http::response([]),
        ]);

        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-ABC']]);
        (new Pinewood())->runScheduledImport();

        Http::assertSent(function (Request $request) {
            return str_contains($request->url(), 'api.pinnacledms.net')
                && $request->header('UserName')[0] === 'testuser'
                && $request->header('Password')[0] === 'testpass'
                && $request->header('OrganisationalUnit_UID')[0] === 'LOC-ABC';
        });
    }

    public function testImportUsesInternetPriceOverRetailPrice()
    {
        Queue::fake();
        $this->setupPinewoodDefaults();

        Http::fake([
            'https://api.pinnacledms.net/*' => Http::response([
                $this->vehiclePayload([
                    'InternetPrice' => 14000,
                    'RetailPrice' => 16000,
                ]),
            ]),
        ]);

        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-1']]);
        (new Pinewood())->runScheduledImport();

        $vehicle = Vehicle::query()->first();
        $this->assertEquals(14000, (int) $vehicle->price);
    }

    public function testImportFallsBackToRetailPriceWhenInternetPriceIsZero()
    {
        Queue::fake();
        $this->setupPinewoodDefaults();

        Http::fake([
            'https://api.pinnacledms.net/*' => Http::response([
                $this->vehiclePayload([
                    'InternetPrice' => 0,
                    'RetailPrice' => 16000,
                ]),
            ]),
        ]);

        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-1']]);
        (new Pinewood())->runScheduledImport();

        $vehicle = Vehicle::query()->first();
        $this->assertEquals(16000, (int) $vehicle->price);
    }

    public function testFilterByPriceExcludesZeroPriceVehicles()
    {
        Queue::fake();
        $this->setupPinewoodDefaults();
        Settings::update('stock-pinewood-sync-with-price', true);

        Http::fake([
            'https://api.pinnacledms.net/*' => Http::response([
                $this->vehiclePayload(['InternetPrice' => 0, 'RetailPrice' => 0]),
                $this->vehiclePayload([
                    'Vehicle_UID' => 'VH-002',
                    'InternetPrice' => 15000,
                    'RetailPrice' => 15000,
                ]),
            ]),
        ]);

        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-1']]);
        (new Pinewood())->runScheduledImport();

        $this->assertEquals(1, Vehicle::query()->count());
        $this->assertTrue(Vehicle::query()->where('uuid', 'VH-002')->exists());
    }

    public function testFilterByPriceDisabledImportsAll()
    {
        Queue::fake();
        $this->setupPinewoodDefaults();
        Settings::update('stock-pinewood-sync-with-price', false);

        Http::fake([
            'https://api.pinnacledms.net/*' => Http::response([
                $this->vehiclePayload(['InternetPrice' => 0, 'RetailPrice' => 0]),
                $this->vehiclePayload(['Vehicle_UID' => 'VH-002', 'InternetPrice' => 15000]),
            ]),
        ]);

        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-1']]);
        (new Pinewood())->runScheduledImport();

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

    public function testVehicleTypeMappingCar()
    {
        Queue::fake();
        $this->setupPinewoodDefaults();

        Http::fake([
            'https://api.pinnacledms.net/*' => Http::response([
                $this->vehiclePayload(['VehicleType' => '1']),
            ]),
        ]);

        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-1']]);
        (new Pinewood())->runScheduledImport();

        $this->assertEquals(VehicleType::CAR->value, Vehicle::query()->first()->type);
    }

    public function testVehicleTypeMappingLCV()
    {
        Queue::fake();
        $this->setupPinewoodDefaults();

        Http::fake([
            'https://api.pinnacledms.net/*' => Http::response([
                $this->vehiclePayload(['VehicleType' => '2']),
            ]),
        ]);

        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-1']]);
        (new Pinewood())->runScheduledImport();

        $vehicle = Vehicle::query()->first();
        $this->assertEquals(VehicleType::LCV->value, $vehicle->type);
        $this->assertFalse((bool) $vehicle->is_vat_applicable);
    }

    public function testVehicleTypeMappingMotorcycle()
    {
        Queue::fake();
        $this->setupPinewoodDefaults();

        Http::fake([
            'https://api.pinnacledms.net/*' => Http::response([
                $this->vehiclePayload(['VehicleType' => '4']),
            ]),
        ]);

        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-1']]);
        (new Pinewood())->runScheduledImport();

        $this->assertEquals(VehicleType::MOTORCYCLE->value, Vehicle::query()->first()->type);
    }

    public function testDemoDetection()
    {
        Queue::fake();
        $this->setupPinewoodDefaults();

        Http::fake([
            'https://api.pinnacledms.net/*' => Http::response([
                $this->vehiclePayload([
                    'Profiles' => [
                        ['Description' => 'Demo'],
                    ],
                ]),
                $this->vehiclePayload([
                    'Vehicle_UID' => 'VH-002',
                    'Profiles' => [
                        ['Description' => 'Ex Demo Vehicle'],
                    ],
                ]),
                $this->vehiclePayload([
                    'Vehicle_UID' => 'VH-003',
                    'Profiles' => [
                        ['Description' => 'Used'],
                    ],
                ]),
            ]),
        ]);

        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-1']]);
        (new Pinewood())->runScheduledImport();

        $this->assertTrue((bool) Vehicle::query()->where('uuid', 'VH-001')->first()->is_demo);
        $this->assertTrue((bool) Vehicle::query()->where('uuid', 'VH-002')->first()->is_demo);
        $this->assertFalse((bool) Vehicle::query()->where('uuid', 'VH-003')->first()->is_demo);
    }

    public function testImageSyncDispatchesJob()
    {
        Queue::fake();
        $this->setupPinewoodDefaults();
        Settings::update('stock-pinewood-sync-images', true);

        Http::fake([
            'https://api.pinnacledms.net/*' => Http::response([
                $this->vehiclePayload([
                    'Images' => [
                        ['ImageURI' => 'https://images.pinewood.com/img1.jpg?'],
                        ['ImageURI' => 'https://images.pinewood.com/img2.jpg?'],
                    ],
                ]),
            ]),
        ]);

        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-1']]);
        (new Pinewood())->runScheduledImport();

        Queue::assertPushed(ImportImagesFromUrlList::class);
    }

    public function testImageSyncDispatchesJobWithCorrectProviderName()
    {
        Queue::fake();
        $this->setupPinewoodDefaults();
        Settings::update('stock-pinewood-sync-images', true);

        Http::fake([
            'https://api.pinnacledms.net/*' => Http::response([
                $this->vehiclePayload([
                    'Images' => [
                        ['ImageURI' => 'https://images.pinewood.com/img1.jpg?'],
                    ],
                ]),
            ]),
        ]);

        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-1']]);
        (new Pinewood())->runScheduledImport();

        Queue::assertPushed(ImportImagesFromUrlList::class, function (ImportImagesFromUrlList $job) {
            $reflection = new \ReflectionProperty($job, 'imageProvider');
            $reflection->setAccessible(true);

            return $reflection->getValue($job) === 'pinewoood';
        });
    }

    public function testImageSyncRemovesImagesDeletedInPinnacle()
    {
        Queue::fake();
        $this->setupPinewoodDefaults();
        Settings::update('stock-pinewood-sync-images', true);

        // Simulate a vehicle that already exists with 3 pinewood images
        $vehicle = Vehicle::factory()->create([
            'uuid' => 'VH-001',
            'stock_provider' => 'pinewood',
        ]);

        $mediaToKeep = Media::factory()->create([
            'src' => 'img1.jpg',
            'source_filename' => 'https://images.pinewood.com/img1.jpg',
            'image_provider' => 'pinewoood',
        ]);
        $mediaToRemove = Media::factory()->create([
            'src' => 'deleted-img.jpg',
            'source_filename' => 'https://images.pinewood.com/deleted-img.jpg',
            'image_provider' => 'pinewoood',
        ]);

        $vehicle->mediaUses()->create(['media_id' => $mediaToKeep->id, 'order' => 0]);
        $vehicle->mediaUses()->create(['media_id' => $mediaToRemove->id, 'order' => 1]);

        $this->assertEquals(2, $vehicle->mediaUses()->count());

        // Pinnacle feed now only has img1 — deleted-img was removed in Pinnacle
        Http::fake([
            'https://api.pinnacledms.net/*' => Http::response([
                $this->vehiclePayload([
                    'Images' => [
                        ['ImageURI' => 'https://images.pinewood.com/img1.jpg?'],
                    ],
                ]),
            ]),
        ]);

        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-1']]);
        (new Pinewood())->runScheduledImport();

        // The deleted image's media_use should have been removed
        $this->assertEquals(1, $vehicle->mediaUses()->count());
        $this->assertEquals($mediaToKeep->id, $vehicle->mediaUses()->first()->media_id);
    }

    public function testImageSyncDoesNotRemoveImagesFromOtherProviders()
    {
        Queue::fake();
        $this->setupPinewoodDefaults();
        Settings::update('stock-pinewood-sync-images', true);

        $vehicle = Vehicle::factory()->create([
            'uuid' => 'VH-001',
            'stock_provider' => 'pinewood',
        ]);

        // Image from a different provider — should NOT be touched
        $otherMedia = Media::factory()->create([
            'source_filename' => 'https://other-provider.com/photo.jpg',
            'image_provider' => 'manual',
        ]);
        $vehicle->mediaUses()->create(['media_id' => $otherMedia->id, 'order' => 0]);

        Http::fake([
            'https://api.pinnacledms.net/*' => Http::response([
                $this->vehiclePayload([
                    'Images' => [
                        ['ImageURI' => 'https://images.pinewood.com/img1.jpg?'],
                    ],
                ]),
            ]),
        ]);

        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-1']]);
        (new Pinewood())->runScheduledImport();

        // The other provider's image should still be attached
        $this->assertTrue(
            $vehicle->mediaUses()->whereHas('media', fn ($q) => $q->where('id', $otherMedia->id))->exists()
        );
    }

    public function testImageSyncNotDispatchedWhenDisabled()
    {
        Queue::fake();
        $this->setupPinewoodDefaults();
        Settings::update('stock-pinewood-sync-images', false);

        Http::fake([
            'https://api.pinnacledms.net/*' => Http::response([
                $this->vehiclePayload([
                    'Images' => [
                        ['ImageURI' => 'https://images.pinewood.com/img1.jpg'],
                    ],
                ]),
            ]),
        ]);

        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-1']]);
        (new Pinewood())->runScheduledImport();

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

    public function testRemoveOldVehiclesNotInFeed()
    {
        Queue::fake();
        $this->setupPinewoodDefaults();

        // Pre-existing vehicle that won't be in the feed
        Vehicle::factory()->create([
            'stock_provider' => 'pinewood',
            'uuid' => 'OLD-VEHICLE',
        ]);

        Http::fake([
            'https://api.pinnacledms.net/*' => Http::response([
                $this->vehiclePayload(),
                $this->vehiclePayload(['Vehicle_UID' => 'VH-002', 'StockNumber' => 'STK-002']),
            ]),
        ]);

        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-1']]);
        (new Pinewood())->runScheduledImport();

        // Old vehicle should be removed, synced ones should exist
        $this->assertFalse(Vehicle::query()->where('uuid', 'OLD-VEHICLE')->exists());
        $this->assertTrue(Vehicle::query()->where('uuid', 'VH-001')->exists());
        $this->assertTrue(Vehicle::query()->where('uuid', 'VH-002')->exists());
    }

    public function testUpdateExistingVehicle()
    {
        Queue::fake();
        $this->setupPinewoodDefaults();

        Vehicle::factory()->create([
            'stock_provider' => 'pinewood',
            'uuid' => 'VH-001',
            'price' => 10000,
        ]);

        Http::fake([
            'https://api.pinnacledms.net/*' => Http::response([
                $this->vehiclePayload(['InternetPrice' => 15995]),
            ]),
        ]);

        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-1']]);
        (new Pinewood())->runScheduledImport();

        $this->assertEquals(1, Vehicle::query()->count());
        $this->assertEquals(15995, (int) Vehicle::query()->first()->price);
    }

    public function testModelYearFromRegistrationDate()
    {
        Queue::fake();
        $this->setupPinewoodDefaults();

        Http::fake([
            'https://api.pinnacledms.net/*' => Http::response([
                $this->vehiclePayload(['RegistrationDate' => '2023-06-15']),
            ]),
        ]);

        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-1']]);
        (new Pinewood())->runScheduledImport();

        $this->assertEquals('2023', Vehicle::query()->first()->manufacture_year);
    }

    public function testRegistrationDateIgnoredWhenInvalid()
    {
        Queue::fake();
        $this->setupPinewoodDefaults();

        Http::fake([
            'https://api.pinnacledms.net/*' => Http::response([
                $this->vehiclePayload(['RegistrationDate' => '0001-01-01T00:00:00']),
            ]),
        ]);

        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-1']]);
        (new Pinewood())->runScheduledImport();

        $this->assertNull(Vehicle::query()->first()->first_registration_date);
    }

    public function testMultipleDealershipLocations()
    {
        Queue::fake();
        $this->setupPinewoodDefaults();

        Http::fake([
            'https://api.pinnacledms.net/*' => Http::response([
                $this->vehiclePayload(),
            ]),
        ]);

        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-A']]);
        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-B']]);
        (new Pinewood())->runScheduledImport();

        // API should be called once per location
        Http::assertSentCount(2);
    }

    public function testFetchReturnsEmptyOnApiFailure()
    {
        Queue::fake();
        $this->setupPinewoodDefaults();

        Http::fake([
            'https://api.pinnacledms.net/*' => Http::response('Server Error', 500),
        ]);

        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-1']]);
        (new Pinewood())->runScheduledImport();

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

    public function testVideoUrlExtractedFromPayload()
    {
        Queue::fake();
        $this->setupPinewoodDefaults();

        Http::fake([
            'https://api.pinnacledms.net/*' => Http::response([
                $this->vehiclePayload([
                    'Videos' => [
                        ['VideoURL' => 'https://youtube.com/watch?v=abc123'],
                    ],
                ]),
            ]),
        ]);

        Dealership::factory()->create(['data' => ['pinewood-location-id' => 'LOC-1']]);
        (new Pinewood())->runScheduledImport();

        $this->assertEquals('https://youtube.com/watch?v=abc123', Vehicle::query()->first()->main_video_url);
    }

    public function testFetchVehiclesFromLocationReturnsEmptyForEmptyLocationId()
    {
        $this->setupPinewoodDefaults();
        $result = (new Pinewood())->fetchVehiclesFromLocation('');
        $this->assertTrue($result->isEmpty());
    }
}
