<?php

namespace App\Imports;

use App\Events\StockSyncFinished;
use App\Facades\Settings;
use App\Master\Models\VehicleMake;
use App\Master\Models\VehicleModel;
use App\Modules\Stock\Models\MotordatStockBranchRecord;
use App\Modules\Stock\Models\MotordatStockFeatures;
use App\Modules\Stock\Models\MotordatStockImages;
use App\Modules\Stock\Models\MotordatStockOptionalEquipment;
use App\Modules\Stock\Models\MotordatStockRecord;
use App\Modules\Stock\Models\MotordatStockTechnicalData;
use App\TaxonomyMap;
use App\Traits\StockSyncTraits;
use App\Modules\Stock\Models\MotordatStockVehicleEquipment;
use App\Traits\EnsuresVehicleAttribute;
use App\VehicleType;
use Carbon\Carbon;
use Exception;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Mtc\ContentManager\Models\MediaFolder;
use Mtc\ContentManager\Models\MediaUse;
use Mtc\MercuryDataModels\Dealership;
use Mtc\MercuryDataModels\Media;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleAttributeValue;
use Mtc\MercuryDataModels\VehicleFinance;
use Mtc\MercuryDataModels\VehicleTrim;

class PeterVardyMotordatToVehicleImport
{
    use DispatchesJobs;
    use StockSyncTraits;
    use EnsuresVehicleAttribute;

    public const NAME = 'motordat';
    private Collection $images;
    private Collection $features;
    private Collection $dealerships;
    private Collection $specs;
    private Collection $optionalEquipment;
    private Collection $standardEquipment;
    private array $vehicleAttributes;

    private ?int $mediaFolderId;

    private array $trims;

    public function __construct()
    {
        $this->setUp();
    }

    public function syncVehicles(): void
    {
        $this->retrieveRequiredVehicleAttributes();

        // dealerships are not currently maintained in the PV stock system.
        // Any dealerships in the stock system are likely to be out of date.
        //$this->syncDealerships();
        $this->loadDealerships();

        MotordatStockRecord::query()
            ->chunk(
                50,
                function (\Illuminate\Database\Eloquent\Collection $records, $index) {
                    dump('chunk ' . $index . ' - ' . (microtime(true) - LARAVEL_START));
                    $this->images = MotordatStockImages::query()
                        ->whereIn('Vehicleid', $records->pluck('Id'))
                        ->get()
                        ->groupBy('Vehicleid');
                    $this->features = MotordatStockFeatures::query()
                        ->whereIn('VehicleId', $records->pluck('Id'))
                        ->get()
                        ->groupBy('VehicleId');
                    $this->specs = MotordatStockTechnicalData::query()
                        ->whereIn('VehicleId', $records->pluck('Id'))
                        ->get()
                        ->groupBy('VehicleId');
                    $this->optionalEquipment = MotordatStockOptionalEquipment::query()
                        ->whereIn('VehicleId', $records->pluck('Id'))
                        ->get()
                        ->groupBy('VehicleId');
                    $this->standardEquipment = MotordatStockVehicleEquipment::query()
                        ->whereIn('VehicleID', $records->pluck('Id'))
                        ->get()
                        ->groupBy('VehicleID');

                    $records->each(function (MotordatStockRecord $record, $index) {
                        try {
                            $this->syncVehicle($record, $index);
                        } catch (\Exception $exception) {
                            Log::warning(tenant('id') . 'stock sync failed for ' . $record->Id, [
                                'message' => $exception->getMessage(),
                                'trace' => $exception->getTrace(),
                            ]);
                        }
                    });
                }
            );

        $this->removeOld(MotordatStockRecord::query()->pluck('Id')->map(fn($id) => (string)$id));

        Event::dispatch(new StockSyncFinished($this->getProviderName()));
    }

    protected function getProviderName(): string
    {
        return 'motordat';
    }

    protected function getDetailsForTaxonomyMap(array $record): array
    {
        return [
            'tenant_id' => tenant('id'),
            'registration_number' => $record['RegistrationNumber'],
            'make' => $record['Make'],
            'model' => $record['Model'],
        ];
    }

    private function retrieveRequiredVehicleAttributes(): void
    {
        $this->vehicleAttributes = [
            'lez_compliant' => $this->getVehicleAttribute('lez_compliant', 'boolean')->toArray(),
            'finance_option' => $this->getVehicleAttribute('finance_option', 'number')->toArray(),
            'coming_soon' => $this->getVehicleAttribute('coming_soon', 'boolean')->toArray(),
            'vehicle_category' => $this->getVehicleAttribute('vehicle_category', 'text')->toArray(),
            'stock_status' => $this->getVehicleAttribute('stock_status', 'text')->toArray(),
        ];
    }

    private function syncVehicle(MotordatStockRecord $record, int $index): void
    {
        $vehicle = $this->storeData($record, $index);
        $this->syncVehicleImages($record, $vehicle);
        $this->syncFinanceExample($record, $vehicle);

        try {
            $this->syncVehicleFeatures($record, $vehicle);
            $this->syncVehicleSpecs($record, $vehicle);
            $this->syncVehicleEquipment($record, $vehicle);
        } catch (\Exception $exception) {
            Log::error('Failed to sync features/specs/equipment from motordat', [
                'tenant' => tenant('id'),
                'vehicle' => $vehicle->id,
                'error' => $exception->getMessage(),
                'trace' => $exception->getTrace(),
            ]);
        }

        if (Settings::get('finance-fetch-only-on-price-change')) {
            if ($vehicle->wasChanged('price')) {
                $this->priceChangeEvents($vehicle, self::NAME);
            }
        } else {
            $this->priceChangeEvents($vehicle, self::NAME);
        }

        $this->syncVehicleAttributes($record, $vehicle);
        $this->populateVehicleTechnicalFields($vehicle);
        $vehicle->save();

        $this->storeUnmappedTaxonomy($vehicle);
    }

    private function populateVehicleTechnicalFields(Vehicle $vehicle): void
    {
        if (
            empty($vehicle->fuelType)
            || (
                strtoupper($vehicle->fuelType->name) != 'ELECTRIC'
                && strtoupper($vehicle->type) != 'LCV'
            )
        ) {
            return;
        }

        $vehicle->specs->each(function ($spec_item) use ($vehicle) {
            switch (strtoupper($spec_item->description)) {
                case 'BATTERY CAPACITY KWH':
                case 'BATTERY CAPACITY IN KWH':
                    $vehicle->battery_capacity_kwh = $this->tryGetNumericValue($spec_item->value);
                    break;

                case 'BATTERY USABLE CAPACITY KWH':
                    $vehicle->battery_usable_capacity_kwh = $this->tryGetNumericValue($spec_item->value);
                    break;

                case 'BATTERY RANGE MILES':
                case 'WLTP - PURE ELECTRIC RANGE (MILES) - COMB':
                case 'WLTP - PURE ELECTRIC RANGE (MILES) - COMB - TEL':
                    $vehicle->battery_range = $this->tryGetNumericValue($spec_item->value);
                    break;

                case 'BATTERY CHARGE FAST TIME (MINUTES)':
                case 'BATTERY CHARGE TYPE 2 TIME (MINUTES)':
                    $vehicle->battery_charge_time = $this->tryGetNumericValue($spec_item->value);
                    break;

                case 'BATTERY CHARGE RAPID TIME (MINUTES)':
                case 'BATTERY CHARGE TYPE 3 TIME (MINUTES)':
                    $vehicle->battery_quick_charge_time = $this->tryGetNumericValue($spec_item->value);
                    break;

                case 'WHEELBASE':
                case 'WHEELBASE MM':
                    $vehicle->wheelbase_mm = $this->tryGetNumericValue($spec_item->value);
                    break;

                case 'WHEELBASE TYPE':
                    $vehicle->wheelbase_type = !empty($spec_item->value) ? $spec_item->value : null;
                    break;

                case 'PAYLOAD':
                case 'PAYLOAD WEIGHTKG':
                    $vehicle->payload_kg = $this->tryGetNumericValue($spec_item->value);
                    break;

                case 'GROSS VEHICLE WEIGHT':
                case 'GROSS VEHICLE WEIGHT KG':
                    $vehicle->gross_vehicle_weight_kg = $this->tryGetNumericValue($spec_item->value);
                    break;

                case 'BHP':
                case 'ENGINE POWER BHP':
                case 'ENGINE POWER - BHP':
                    $vehicle->bhp = $this->tryGetNumericValue($spec_item->value);
                    break;

                default:
                    break;
            }
        });
    }

    /**
     * @param mixed $input
     * @return int|string|null
     */
    private function tryGetNumericValue(mixed $input)
    {
        if (empty($input) || !is_numeric($input)) {
            return null;
        }

        return $input;
    }

    private function storeData(MotordatStockRecord $record): Vehicle
    {
        if ($record->OdometerType === 'Miles') {
            $odometerMi = $record->Odometer;
            $odometerKm = (new Vehicle())->milesToKm($record->Odometer);
        } else {
            $odometerKm = $record->Odometer;
            $odometerMi = (new Vehicle())->kmToMiles($record->Odometer);
        }

        try {
            $registrationDate = Carbon::parse($record->RegistrationDate);
            $registrationDate = $registrationDate->year < 1900 ? null : $registrationDate->format('Y-m-d');
        } catch (Exception $exception) {
            $registrationDate = null;
        }

        $manufactureYear = $record->CapModelYear ?: null;
        if (!is_numeric($manufactureYear)) {
            $manufactureYear = null;
        }

        $vehicle_data = $record->toArray();

        $make_id = $this->getMappedTaxonomy(TaxonomyMap::MAKE, $record->Make, $vehicle_data);
        $model_id = $this->getMappedTaxonomy(TaxonomyMap::MODEL, $record->Model, $vehicle_data, $make_id);

        $is_new = $record->NewUsed === 'N';

        /** @var Vehicle $vehicle */
        $vehicle = Vehicle::query()
            ->updateOrCreate([
                'stock_provider' => self::NAME,
                'uuid' => $record->Id,
            ], [
                'stock_provider' => self::NAME,
                'is_published' => $record->IsVisible,
                'uuid' => $record->Id,
                'title' => $this->getTitle($record, $vehicle_data),
                'registration_number' => $record->RegistrationNumber,
                'first_registration_date' => $registrationDate,
                'cap_id' => $record->CapId,
                'make_id' => $make_id,
                'model_id' => $model_id,
                'derivative' => $record->Derivative,
                'colour' => ucwords(strtolower($record->PaintColour)),
                'fuel_type_id' => $this->getMappedTaxonomy(TaxonomyMap::FUEL_TYPE, $record->FuelType, $vehicle_data),
                'body_style_id' => $this->getMappedTaxonomy(TaxonomyMap::BODY_STYLE, $record->BodyStyle, $vehicle_data),
                'engine_size_cc' => $record->EngineSize * 1000,
                'dealership_id' => $this->getDealership($record),
                'vin' => $record->ChassisNumber,
                'is_new' => $is_new,
                'is_demo' => $record->Demo,
                'is_vat_applicable' => $this->getVatApplicable($record),
                'transmission_id' => $this->getMappedTaxonomy(
                    TaxonomyMap::TRANSMISSION,
                    $record->TransmissionType,
                    $vehicle_data
                ),
                'manufacture_year' => $manufactureYear,
                'odometer_mi' => $odometerMi,
                'odometer_km' => $odometerKm,
                'previous_owner_count' => $record->PreviousOwners,
                'price' => $record->VehicleSalePrice,
                'original_price' => $record->PreviousHighestPrice,
                'previous_price' => $record->PreviousPrice,
                'rrp_price' => $is_new ? $record->MRRP : $record->RRP,
                'co2' => $record->Co2,
                'door_count' => $record->Doors,
                'seats' => $record->Seats,
                'description' => $record->ManagersComments,
                'main_video_url' => $record->YouTubeVideo,
                'type' => $this->mapVehicleType($record),
                'mpg' => $record->Mpg,
                'available_date' => $this->getAvailableDate($record->AvailableDate),
//                'deleted_at' => $this->isVehicleDeleted($vehicle_data) ? Carbon::now() : null,
                'trim' => $this->getTrimFromString($record->Derivative, $make_id, $model_id),
            ]);
        return $vehicle;
    }

    /**
     * @param string $string
     * @param int $make_id
     * @return string|null
     */
    private function getTrimFromString(string $string, ?int $make_id, ?int $model_id): ?string
    {
        if (empty($make_id) || empty($model_id)) {
            return null;
        }

        $string = strtolower($string);

        foreach ($this->getTrims($make_id, $model_id) as $trim) {
            $trim_name = strtolower($trim->name);

            /*
             * Trim strings could exist as part of other words in the search string.
             * To avoid matching e.g. 'i' in 'automatic', look first for ' my_trim ', with whitespace on either side.
             * Fall back to strings starting or ending with my_trim.
             */
            if (
                Str::contains($string, ' ' . $trim_name . ' ')
                || Str::startsWith($string, $trim_name)
                || Str::endsWith($string, $trim_name)
            ) {
                return $trim_name;
            }
        }

        return null;
    }

    private function getTrims(int $make_id, int $model_id)
    {
        if (!isset($this->trims)) {
            $this->trims = [];
        }

        if (!array_key_exists($make_id, $this->trims)) {
            $this->trims[$make_id] = [];
        }

        if (!array_key_exists($model_id, $this->trims[$make_id])) {
            $this->trims[$make_id][$model_id] = VehicleTrim::query()
                ->where('make_id', '=', $make_id)
                ->where(function ($query) use ($model_id) {
                    $query->where('model_id', '=', $model_id)
                    ->orWhereNull('model_id');
                })
                ->get() ?? null;
        }

        return $this->trims[$make_id][$model_id];
    }

    private function getAvailableDate($available_date = null)
    {
        if (
            empty($available_date)
            || strtoupper($available_date) == 'NULL'
        ) {
            return null;
        }

        return $available_date;
    }

    /**
     * return a boolean indicating if VAT is applicable to the vehicle.
     *
     * @param MotordatStockRecord $record
     * @return bool
     */
    private function getVatApplicable(MotordatStockRecord $record): bool
    {
        // If PlusVat is false, VAT is included
        return !$record->PlusVat;
    }

    private function getTitle(MotordatStockRecord $record, $vehicle_data = []): string
    {
        $make_id = $this->getMappedTaxonomy(TaxonomyMap::MAKE, $record->Make, $vehicle_data);
        $make = VehicleMake::where('id', '=', $make_id)->first();
        $make_name = $make ? $make->name : '';

        $model_id = $this->getMappedTaxonomy(TaxonomyMap::MODEL, $record->Model, $vehicle_data);
        $model = VehicleModel::where('id', '=', $model_id)->first();
        $model_name = $model ? $model->name : '';

        return trim($make_name . ' ' . $model_name);
    }

    private function syncVehicleImages(MotordatStockRecord $record, Vehicle $vehicle): void
    {
        $collection = ($this->images[$record->Id] ?? collect());
        // Sometimes there are not indexNumber 1 in stock, find lowest index number to assign primary
        $lowest_index = $collection->sortBy('IndexNumber')->first()?->IndexNumber;
        $images = $collection
            ->map(fn($image) => [
                'src' => $image->Filename,
                'image_provider' => self::NAME,
                'type' => 'image',
                'external' => true,
                'path' => '',
                'folder_id' => $this->getMediaFolderId(),
                'upload_date' => Carbon::now()->format('Y-m'),
            ])
            ->tap(function (Collection $allImages) {
                $allImages->chunk(100)->each(fn(Collection $chunk) => Media::query()->upsert(
                    $chunk->toArray(),
                    ['src', 'image_provider']
                ));
            });

        $mediaRecords = Media::query()
            ->whereIn('src', $images->pluck('src'))
            ->where('image_provider', self::NAME)
            ->select(['id', 'src'])
            ->get();

        $mediaRecords->map(fn($media) => [
            'media_id' => $media->id,
            'owner_type' => 'vehicle',
            'owner_id' => $vehicle->id,
            'order' => $collection->where('Filename', $media->src)->sortBy('IndexNumber')->first()?->IndexNumber ?? 0,
            'primary' => $collection
                    ->where('Filename', $media->src)
                    ->sortBy('IndexNumber')
                    ->first()
                    ?->IndexNumber == $lowest_index,
        ])
            ->tap(fn(Collection $uses) => MediaUse::query()->upsert(
                $uses->toArray(),
                ['media_id', 'owner_type', 'owner_id']
            ));

        $vehicle->mediaUses()
            ->whereNotIn('media_id', $mediaRecords->pluck('id'))
            ->delete();
    }


    private function syncVehicleFeatures(MotordatStockRecord $record, Vehicle $vehicle): void
    {
        $record_features = ($this->features[$record->Id] ?? collect())
            ->map(fn($feature) => [
                'vehicle_id' => $vehicle->id,
                'vehicle_type' => 'vehicle',
                'name' => $feature->Description,
                'slug' => Str::slug($feature->Description),
            ])
            ->tap(fn(Collection $features) => $vehicle->features()->upsert(
                $features->toArray(),
                ['vehicle_id', 'name']  // note that this array is ignored for mysql
            ));

        // delete any features which are not present in the stock record
        $vehicle->features()->whereNotIn('name', collect($record_features)->pluck('name'))->delete();
    }

    private function syncVehicleSpecs(MotordatStockRecord $record, Vehicle $vehicle): void
    {
        $record_specs = ($this->specs[$record->Id] ?? collect())
            ->map(fn($spec) => [
                'vehicle_id' => $vehicle->id,
                'vehicle_type' => 'vehicle',
                'category' => $spec->Category,
                'description' => $spec->Description,
                'value' => $spec->Value,
            ])
            ->tap(fn(Collection $specs) => $vehicle->specs()->upsert(
                $specs->toArray(),
                ['vehicle_id', 'category', 'description']
            ));

        $vehicle->specs()->whereNotIn('description', collect($record_specs)->pluck('description'))->delete();
    }

    private function syncVehicleEquipment(MotordatStockRecord $record, Vehicle $vehicle): void
    {
        $vehicle->equipment()->delete();
        $record_standard_equipment = ($this->standardEquipment[$record->Id] ?? collect())
            ->map(fn($entry) => [
                'vehicle_id' => $vehicle->id,
                'vehicle_type' => 'vehicle',
                'type' => 'Standard',
                'category' => $entry->GroupDescription,
                'description' => $entry->ItemDescription,
            ])
            ->when($vehicle->id == 8942, fn($c) => $c->dd())
            ->tap(fn(Collection $equipment) => $vehicle->equipment()->upsert(
                $equipment->toArray(),
                ['vehicle_id', 'type', 'description']
            ));

        $record_optional_equipment = ($this->optionalEquipment[$record->Id] ?? collect())
            ->map(fn($entry) => [
                'vehicle_id' => $vehicle->id,
                'vehicle_type' => 'vehicle',
                'type' => 'Optional',
                'category' => $entry->CategoryDescription,
                'description' => $entry->ItemDescription,
                'price' => $entry->BasicPrice,
                'vat_amount' => $entry->VatPrice,
            ])
            ->tap(fn(Collection $equipment) => $vehicle->equipment()->upsert(
                $equipment->toArray(),
                ['vehicle_id', 'type', 'description']
            ));

        // delete any standard equipment items that are not present in the stock record
        $vehicle->equipment()
            ->where('type', '=', 'Standard')
            ->whereNotIn(
                'description',
                collect($record_standard_equipment)->pluck('description')
            )
            ->delete();

        // delete any optional equipment items that are not present in the stock record
        $vehicle->equipment()
            ->where('type', '=', 'Optional')
            ->whereNotIn(
                'description',
                collect($record_optional_equipment)->pluck('description')
            )
            ->delete();
    }

    private function syncVehicleAttributes(MotordatStockRecord $record, Vehicle $vehicle): void
    {
        $this->setVehicleAttributes(
            [
                'lez_compliant' => $record->LezCompliant,
                'finance_option' => $record->FinanceOption,
                'coming_soon' => $record->ComingSoon,
                'vehicle_category' => $record->MarketingCategory,
                'stock_status' => $record->StockType,
            ],
            $vehicle
        );
    }

    private function setUp(): void
    {
        Config::set('database.connections.motordat.host', Settings::get('stock-motordat-host'));
        Config::set('database.connections.motordat.username', Settings::get('stock-motordat-username'));
        Config::set('database.connections.motordat.password', Settings::get('stock-motordat-password'));
        Config::set('database.connections.motordat.database', Settings::get('stock-motordat-database'));
    }

    private function getDealership(MotordatStockRecord $record): ?int
    {
        $dealerCode = $record->DealerCode;

        // Vanz and Carz vehicles share the same dealer code in Motordat.
        // We want to separate them here so identify Carz LCVs and modify their dealer code.
        if ($this->mapVehicleType($record) == VehicleType::LCV->value) {
            $dealerCode .= '-VANZ';
        }

        if (
            strtoupper($record->Make) == 'OMODA'
            && $dealerCode == 'VM'
            && $this->dealerships->has('VM-OMODA')
        ) {
            $dealerCode = 'VM-OMODA';
        }

        if (
            strtoupper($record->Make) == 'JAECOO'
            && $dealerCode == 'VM'
            && $this->dealerships->has('VM-JAECOO')
        ) {
            $dealerCode = 'VM-JAECOO';
        }

        return $this->dealerships[$dealerCode]->id ?? null;
    }

    private function mapVehicleType(MotordatStockRecord $record)
    {
        return match ($record->VehicleType) {
            4 => VehicleType::LCV->value,
            default => VehicleType::CAR->value,
        };
    }

    private function syncDealerships(): void
    {
        $this->dealerships = MotordatStockBranchRecord::query()
            ->get()
            ->map(fn(MotordatStockBranchRecord $branch) => $this->syncDealership($branch))
            ->keyBy('location_stock');
    }

    private function loadDealerships(): void
    {
        $this->dealerships = Dealership::query()
            ->get()
            ->keyBy('location_stock');
    }

    private function syncDealership(MotordatStockBranchRecord $branch): Model
    {
        return Dealership::query()
            ->firstOrCreate(
                ['location_stock' => $branch->BranchCode],
                [
                    'location_stock' => $branch->BranchCode,
                    'name' => $branch->BranchName,
                    'address1' => $branch->BranchAddress1,
                    'address2' => $branch->BranchAddress2,
                    'city' => $branch->BranchTown,
                    'postcode' => $branch->BranchPostcode,
                    'data' => [
                        'ivendi' => $branch->IvendiId,
                        'auto-trader-location-id' => $branch->AutoTraderId,
                        'branch-type' => $branch->BranchType,
                        'include-in-carstore' => $branch->IncludeInCarstore,
                        'branch-id' => $branch->BranchId
                    ]
                ]
            );
    }

    private function syncFinanceExample(MotordatStockRecord $record, Vehicle $vehicle): ?Model
    {
        $importer_finance_example_query = VehicleFinance::query()
            ->where('vehicle_id', '=', $vehicle->id)
            ->where('provider', '=', 'import');

        if ($importer_finance_example_query->exists()) {
            // we have previously imported hard coded finance examples for this vehicle.
            // delete old importer finance examples for this vehicle.
            $importer_finance_example_query->delete();

            // delete vehicle fields
            $vehicle->update([
                    'monthly_price' => null,
                    'monthly_cost_type' => null,
                    'deposit' => null
                ]);
        }

        if (empty($record->FinanceType) || !$this->isEligibleForFinanceExample($vehicle)) {
            // there is no importer finance to store
            return null;
        }

        $vehicle->update([
            'monthly_price' => $record->MonthlyPayment,
            'monthly_cost_type' => strtolower($record->FinanceType),
            'deposit' => $record->TotalDeposit
        ]);

        // add new finance example
        return VehicleFinance::query()->create([
            'vehicle_id' => $vehicle->id,
            'provider' => 'import',
            'finance_type' => $record->FinanceType,
            'term' => $record->Term,
            'annual_mileage' => $record->AnnualMileage,
            'monthly_price' => $record->MonthlyPayment,
            'cash_price' => $record->CashPrice,
            'customer_deposit' => $record->CustomerDeposit,
            'dealer_deposit' => $record->DealerDepositContribution,
            'total_deposit' => $record->TotalDeposit,
            'total_credit_amount' => $record->AmountOfCredit,
            'payable_amount' => $record->TotalAmountPayable,
            'final_payment' => $record->OptionalFinalPayment,
            'option_to_purchase_fee' => $record->OptionToPurchaseFee,
            'apr' => $record->APR,
            'interest_rate' => $record->RateOfInterest,
        ]);
    }

    private function isEligibleForFinanceExample(Vehicle $vehicle): bool
    {
        return (bool)$vehicle->is_new;
    }

    private function getMediaFolderId(): ?int
    {
        if (!isset($this->mediaFolderId)) {
            $this->mediaFolderId = MediaFolder::query()
                ->where('name', 'Vehicles')
                ->first()
                ?->id;
        }

        return $this->mediaFolderId;
    }
}
