<?php

namespace Tests\Tenant;

use App\Facades\Settings;
use App\Jobs\ImportMotorsVehicleBatch;
use App\Modules\Stock\Motors;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Facades\Storage;
use Mockery;
use Mtc\ContentManager\Contracts\Media;
use Mtc\MercuryDataModels\Dealership;
use Mtc\MercuryDataModels\Vehicle;
use Tests\TenantTestCase;

class MotorsTest 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-motors-enabled', true);
        $this->makeSetting('stock-motors-ftp-username', 'testuser', 'string');
        $this->makeSetting('stock-motors-ftp-password', 'testpass', 'string');
        $this->makeSetting('stock-motors-sync-with-price', false);
        $this->makeSetting('stock-motors-sync-min-price', '', 'string');
        $this->makeSetting('stock-motors-sync-max-age', '', 'string');
        $this->makeSetting('stock-motors-sync-max-mileage', '', 'string');
    }

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

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

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

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

    public function testExternalMediaUrl()
    {
        $motors = new Motors();

        $media = Mockery::mock(Media::class)->makePartial();
        $media->src = '/path/to/image.jpg';

        $url = $motors->url($media, 'large');
        $this->assertEquals('https://origin-resizer.images.autoexposure.co.uk/path/to/image.jpg', $url);
    }

    public function testExternalMediaUrlTrimsLeadingSlash()
    {
        $motors = new Motors();

        $media = Mockery::mock(Media::class)->makePartial();
        $media->src = '//double/slash/image.jpg';

        $url = $motors->url($media, 'medium');
        $this->assertStringNotContainsString('///', $url);
    }

    public function testFilterByPriceAllowsAllWhenDisabled()
    {
        $this->setupDefaults();
        Settings::update('stock-motors-sync-with-price', false);

        $motors = new Motors();
        $method = new \ReflectionMethod(Motors::class, 'filterByPrice');
        $method->setAccessible(true);

        $vehicle = collect(['price' => 0]);
        $this->assertTrue($method->invoke($motors, $vehicle));
    }

    public function testFilterByPriceRejectsZeroPriceWhenEnabled()
    {
        $this->setupDefaults();
        Settings::update('stock-motors-sync-with-price', true);

        $motors = new Motors();
        $method = new \ReflectionMethod(Motors::class, 'filterByPrice');
        $method->setAccessible(true);

        $vehicle = collect(['price' => 0]);
        $this->assertFalse($method->invoke($motors, $vehicle));
    }

    public function testFilterByPriceAcceptsPositivePriceWhenEnabled()
    {
        $this->setupDefaults();
        Settings::update('stock-motors-sync-with-price', true);

        $motors = new Motors();
        $method = new \ReflectionMethod(Motors::class, 'filterByPrice');
        $method->setAccessible(true);

        $vehicle = collect(['price' => 15000]);
        $this->assertTrue($method->invoke($motors, $vehicle));
    }

    public function testFilterByPriceRejectsBelowMinPrice()
    {
        $this->setupDefaults();
        Settings::update('stock-motors-sync-min-price', 5000);

        $motors = new Motors();
        $method = new \ReflectionMethod(Motors::class, 'filterByPrice');
        $method->setAccessible(true);

        $vehicle = collect(['price' => 3000]);
        $this->assertFalse($method->invoke($motors, $vehicle));
    }

    public function testFilterByPriceAcceptsAboveMinPrice()
    {
        $this->setupDefaults();
        Settings::update('stock-motors-sync-min-price', 5000);

        $motors = new Motors();
        $method = new \ReflectionMethod(Motors::class, 'filterByPrice');
        $method->setAccessible(true);

        $vehicle = collect(['price' => 10000]);
        $this->assertTrue($method->invoke($motors, $vehicle));
    }

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

        $motors = new Motors();
        $method = new \ReflectionMethod(Motors::class, 'filterByAge');
        $method->setAccessible(true);

        $vehicle = collect(['regyear' => '2000']);
        $this->assertTrue($method->invoke($motors, $vehicle));
    }

    public function testFilterByAgeRejectsOldVehicle()
    {
        $this->setupDefaults();
        Settings::update('stock-motors-sync-max-age', 5);

        $motors = new Motors();
        $method = new \ReflectionMethod(Motors::class, 'filterByAge');
        $method->setAccessible(true);

        $oldYear = (int) date('Y') - 10;
        $vehicle = collect(['regyear' => (string) $oldYear]);
        $this->assertFalse($method->invoke($motors, $vehicle));
    }

    public function testFilterByAgeAcceptsRecentVehicle()
    {
        $this->setupDefaults();
        Settings::update('stock-motors-sync-max-age', 5);

        $motors = new Motors();
        $method = new \ReflectionMethod(Motors::class, 'filterByAge');
        $method->setAccessible(true);

        $recentYear = (int) date('Y') - 2;
        $vehicle = collect(['regyear' => (string) $recentYear]);
        $this->assertTrue($method->invoke($motors, $vehicle));
    }

    public function testFilterByAgeRejectsEmptyYear()
    {
        $this->setupDefaults();
        Settings::update('stock-motors-sync-max-age', 5);

        $motors = new Motors();
        $method = new \ReflectionMethod(Motors::class, 'filterByAge');
        $method->setAccessible(true);

        $vehicle = collect(['regyear' => '']);
        $this->assertFalse($method->invoke($motors, $vehicle));
    }

    public function testFilterByAgeRejectsFutureYear()
    {
        $this->setupDefaults();
        Settings::update('stock-motors-sync-max-age', 5);

        $motors = new Motors();
        $method = new \ReflectionMethod(Motors::class, 'filterByAge');
        $method->setAccessible(true);

        $futureYear = (int) date('Y') + 1;
        $vehicle = collect(['regyear' => (string) $futureYear]);
        $this->assertFalse($method->invoke($motors, $vehicle));
    }

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

        $motors = new Motors();
        $method = new \ReflectionMethod(Motors::class, 'filterByMileage');
        $method->setAccessible(true);

        $vehicle = collect(['mileage' => 999999]);
        $this->assertTrue($method->invoke($motors, $vehicle));
    }

    public function testFilterByMileageRejectsHighMileage()
    {
        $this->setupDefaults();
        Settings::update('stock-motors-sync-max-mileage', 50000);

        $motors = new Motors();
        $method = new \ReflectionMethod(Motors::class, 'filterByMileage');
        $method->setAccessible(true);

        $vehicle = collect(['mileage' => 60000]);
        $this->assertFalse($method->invoke($motors, $vehicle));
    }

    public function testFilterByMileageAcceptsLowMileage()
    {
        $this->setupDefaults();
        Settings::update('stock-motors-sync-max-mileage', 50000);

        $motors = new Motors();
        $method = new \ReflectionMethod(Motors::class, 'filterByMileage');
        $method->setAccessible(true);

        $vehicle = collect(['mileage' => 30000]);
        $this->assertTrue($method->invoke($motors, $vehicle));
    }

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

        $motors = new Motors();
        $method = new \ReflectionMethod(Motors::class, 'filterVehicle');
        $method->setAccessible(true);

        $vehicle = collect(['price' => 5000, 'regyear' => date('Y'), 'mileage' => 1000, 'vehicletype' => 'BIKE']);
        $this->assertFalse($method->invoke($motors, $vehicle));
    }

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

        $motors = new Motors();
        $method = new \ReflectionMethod(Motors::class, 'filterVehicle');
        $method->setAccessible(true);

        $vehicle = collect(['price' => 5000, 'regyear' => date('Y'), 'mileage' => 1000, 'vehicletype' => 'CAR']);
        $this->assertTrue($method->invoke($motors, $vehicle));
    }

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

        Dealership::factory()->create(['data' => ['motors-location-id' => 'LOC-1']]);

        $motors = new Motors();
        $batch = collect([
            collect([
                'vehicleid' => 'V001',
                'price' => 15000,
                'regyear' => date('Y'),
                'mileage' => 10000,
                'vehicletype' => 'CAR',
            ]),
        ]);

        $motors->collection($batch, 'test.csv');

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

    public function testCollectionDoesNotDispatchWhenAllFiltered()
    {
        Queue::fake();
        $this->setupDefaults();
        Settings::update('stock-motors-sync-with-price', true);

        $motors = new Motors();
        $batch = collect([
            collect([
                'vehicleid' => 'V001',
                'price' => 0,
                'regyear' => date('Y'),
                'mileage' => 10000,
                'vehicletype' => 'CAR',
            ]),
        ]);

        $motors->collection($batch, 'test.csv');

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

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

        Storage::fake('motors-sftp');
        Storage::fake('local');

        Storage::disk('motors-sftp')->put('stock.csv', "VehicleId,Price\nV001,15000\n");
        Dealership::factory()->create(['data' => ['motors-location-id' => 'LOC-1']]);

        (new Motors())->runScheduledImport();

        // Verify file was copied from SFTP to local and then cleaned up
        Storage::disk('local')->assertMissing('stock.csv');
    }

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

        // Create a vehicle that should be removed
        Vehicle::factory()->create([
            'stock_provider' => 'motors',
            'uuid' => 'OLD-V001',
            'was_recently_synced' => false,
            'pending_stock_sync' => null,
        ]);

        Storage::fake('motors-sftp');
        Storage::fake('local');

        Storage::disk('motors-sftp')->put('stock.csv', "VehicleId,Price\nV001,15000\n");
        Dealership::factory()->create(['data' => ['motors-location-id' => 'LOC-1']]);

        (new Motors())->runScheduledImport();

        $this->assertFalse(Vehicle::query()->where('uuid', 'OLD-V001')->exists());
    }
}
