<?php

namespace App\Jobs;

use App\Events\NewVehicleImported;
use App\Events\VehiclePriceChangedDuringStockSync;
use App\Events\VehicleUpdatedFromImport;
use App\Facades\Settings;
use App\TaxonomyMap;
use App\Traits\ImportChecksConditions;
use App\Traits\MapsTaxonomies;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Log;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleSpecType;
use Mtc\MercuryDataModels\VehicleStandardEquipment;
use Mtc\MercuryDataModels\VehicleTechnicalData;
use Mtc\MercuryDataModels\VehicleType;

class ImportDealerKitVehicleBatch implements ShouldQueue
{
    use Dispatchable;
    use InteractsWithQueue;
    use Queueable;
    use SerializesModels;
    use MapsTaxonomies;
    use ImportChecksConditions;

    public const NAME = 'DealerKit';
    public const PROVIDER_KEY = 'dealerkit';

    public $tries = 2;

    private Collection $existingVehicles;

    public function __construct(
        private readonly Collection $batch,
        private readonly ?string $dealershipId,
    ) {
        $this->onQueue('sync');
    }

    public function handle(): void
    {
        $ids = $this->batch->pluck('id')->filter()->all();

        $this->existingVehicles = Vehicle::query()
            ->withTrashed()
            ->where('stock_provider', self::PROVIDER_KEY)
            ->whereIn('uuid', $ids)
            ->get();

        $this->batch->each(function (array $data): void {
            try {
                $this->syncVehicle($data);
            } catch (\Exception $exception) {
                if (!str_contains($exception->getMessage(), 'Duplicate entry')) {
                    Log::error('DealerKit sync failed: ' . $exception->getMessage(), [
                        'tenant' => tenant('id'),
                        'vehicle_id' => $data['id'] ?? null,
                        'trace' => $exception->getTraceAsString(),
                    ]);
                }
            }
        });
    }

    private function syncVehicle(array $data): void
    {
        $uuid = $data['id'] ?? null;
        if (empty($uuid)) {
            return;
        }

        $vehicleData = $data['vehicle'] ?? [];

        /** @var Vehicle $vehicle */
        $vehicle = $this->existingVehicles->firstWhere('uuid', $uuid) ?? new Vehicle([
            'stock_provider' => self::PROVIDER_KEY,
            'uuid' => $uuid,
        ]);

        if ($vehicle->trashed()) {
            $vehicle->restore();
        }

        $makeId = $this->getMappedTaxonomy(TaxonomyMap::MAKE, $vehicleData['manufacturer'] ?? null, $data);

        $priorPrice = $vehicle->price;

        $vehicle->fill([
            'title' => trim(($vehicleData['manufacturer'] ?? '') . ' ' . ($vehicleData['model'] ?? '')),
            'derivative' => $vehicleData['derivative'] ?? null,
            'trim' => $vehicleData['trim'] ?? null,
            'vin' => $vehicleData['vin'] ?? null,
            'registration_number' => $vehicleData['registration'] ?? null,
            'cap_id' => $vehicleData['cap_id'] ?? null,
            'type' => $this->mapVehicleType($vehicleData['type'] ?? null),
            'is_new' => ($vehicleData['condition'] ?? null) === 'new',
            'colour' => $vehicleData['colour'] ?? null,
            'make_id' => $makeId,
            'model_id' => $this->getMappedTaxonomy(
                TaxonomyMap::MODEL,
                $vehicleData['model'] ?? null,
                $data,
                $makeId
            ),
            'body_style_id' => $this->getMappedTaxonomy(
                TaxonomyMap::BODY_STYLE,
                $vehicleData['body_type'] ?? null,
                $data
            ),
            'transmission_id' => $this->getMappedTaxonomy(
                TaxonomyMap::TRANSMISSION,
                $vehicleData['transmission_type'] ?? null,
                $data
            ),
            'fuel_type_id' => $this->getMappedTaxonomy(
                TaxonomyMap::FUEL_TYPE,
                $vehicleData['fuel_type'] ?? null,
                $data
            ),
            'drivetrain_id' => $this->getMappedTaxonomy(
                TaxonomyMap::DRIVETRAIN,
                $vehicleData['drive_train'] ?? null,
                $data
            ),
            'dealership_id' => $this->dealershipId,
            'previous_owner_count' => $this->intOrNull($data['owners'] ?? null),
            'price' => $data['prices']['advertised']['amount'] ?? $data['prices']['cash']['amount'] ?? null,
            'is_vat_applicable' => $this->mapVatApplicable($data['prices']['advertised']['vat_status'] ?? null),
            'description' => $data['advertising']['comments'] ?? null,
            'attention_grabber' => $data['advertising']['attention_grabber'] ?? null,
            'odometer_mi' => $this->intOrNull($vehicleData['mileage'] ?? null),
            'door_count' => $this->intOrNull($vehicleData['number_of_doors'] ?? null),
            'seats' => $this->intOrNull($vehicleData['number_of_seats'] ?? null),
            'manufacture_year' => $this->intOrNull($vehicleData['year'] ?? null),
            'engine_size_cc' => $this->intOrNull($vehicleData['engine_size'] ?? null),
            'co2' => $this->intOrNull($vehicleData['co2'] ?? null),
            'mpg' => $this->intOrNull($vehicleData['mpg_combined'] ?? null),
            'bhp' => $this->intOrNull($vehicleData['bhp'] ?? null),
            'wheelbase_mm' => $this->intOrNull($vehicleData['wheelbase_mm'] ?? null),
            'battery_range' => $this->intOrNull($vehicleData['battery_range_miles'] ?? null),
            'battery_capacity_kwh' => $vehicleData['battery_capacity_kwh'] ?? null,
            'first_registration_date' => $this->parseDate($vehicleData['registration_date'] ?? null),
            'was_recently_synced' => true,
            'pending_stock_sync' => null,
        ]);

        $vehicle->is_published = $this->shouldBePublished(
            fn () => $vehicle->is_published ?? true,
            self::PROVIDER_KEY,
            $vehicle,
        ) ?? false;

        $priceChanged = $vehicle->exists
            && !is_null($priorPrice)
            && round((float) $priorPrice, 2) !== round((float) $vehicle->price, 2);

        if ($priceChanged) {
            $vehicle->previous_price = $priorPrice;
        }

        $vehicle->save();

        if ($priceChanged) {
            Event::dispatch(new VehiclePriceChangedDuringStockSync($vehicle, self::PROVIDER_KEY));
        }

        if ($this->shouldSyncImages($vehicle, $data['media'] ?? [])) {
            $this->importImages($vehicle, $data['media'] ?? []);
        }

        $this->syncSpecifications($vehicle, $data['vehicle']['specifications'] ?? []);

        $this->storeUnmappedTaxonomy($vehicle);

        $event = $vehicle->wasRecentlyCreated
            ? new NewVehicleImported($vehicle, $data, self::PROVIDER_KEY)
            : new VehicleUpdatedFromImport($vehicle, $data, self::PROVIDER_KEY);

        Event::dispatch($event);
    }

    /**
     * Ordered list of image URLs in the feed for this vehicle, cover image first.
     */
    private function feedImageUrls(array $media): Collection
    {
        $urls = collect($media['images'] ?? [])
            ->pluck('url')
            ->filter()
            ->values();

        if (!empty($media['cover_image']['url'] ?? null)) {
            $urls = $urls->prepend($media['cover_image']['url'])->unique()->values();
        }

        return $urls;
    }

    /**
     * Only pull images when enabled and the feed's image set has changed since the
     * last sync, matching the checksum gate used by the other stock providers.
     */
    private function shouldSyncImages(Vehicle $vehicle, array $media): bool
    {
        return Settings::get('stock-dealerkit-sync-images')
            && empty($vehicle->deleted_at)
            && $this->imageChecksumMismatch($this->feedImageUrls($media)->toArray(), $vehicle);
    }

    private function importImages(Vehicle $vehicle, array $media): void
    {
        ImportImageUrlList::dispatch(
            $this->feedImageUrls($media),
            $vehicle,
            ['imageProvider' => self::PROVIDER_KEY],
        );
    }

    private function syncSpecifications(Vehicle $vehicle, array $specifications): void
    {
        $this->syncEquipmentItems(
            $vehicle,
            $specifications['standard']['items'] ?? [],
            VehicleSpecType::STANDARD_EQUIPMENT,
        );
        $this->syncEquipmentItems(
            $vehicle,
            $specifications['options']['items'] ?? [],
            VehicleSpecType::OPTIONAL_EQUIPMENT,
        );
        $this->syncTechnicalItems(
            $vehicle,
            $specifications['technical']['items'] ?? [],
        );
    }

    private function syncEquipmentItems(Vehicle $vehicle, array $items, VehicleSpecType $type): void
    {
        foreach ($items as $item) {
            $description = $item['label'] ?? null;
            if (empty($description)) {
                continue;
            }

            VehicleStandardEquipment::query()->updateOrCreate(
                [
                    'vehicle_id' => $vehicle->id,
                    'vehicle_type' => 'vehicle',
                    'description' => $description,
                ],
                [
                    'type' => $type->value,
                    'code' => $item['generic_label'] ?? null,
                    'category' => $item['group'] ?? null,
                    'price' => $this->priceOrNull($item['price'] ?? null),
                ],
            );
        }
    }

    private function syncTechnicalItems(Vehicle $vehicle, array $items): void
    {
        foreach ($items as $item) {
            $description = $item['label'] ?? null;
            $value = $item['description'] ?? null;
            if (empty($description) || $value === null || $value === '') {
                continue;
            }

            VehicleTechnicalData::query()->updateOrCreate(
                [
                    'vehicle_id' => $vehicle->id,
                    'vehicle_type' => 'vehicle',
                    'description' => $description,
                ],
                [
                    'code' => $item['generic_label'] ?? null,
                    'value' => $value,
                    'category' => $item['group'] ?? null,
                ],
            );
        }
    }

    private function priceOrNull(mixed $value): ?float
    {
        if ($value === null || $value === '' || !is_numeric($value)) {
            return null;
        }
        return (float) $value;
    }

    private function mapVehicleType(?string $type): string
    {
        /*
         * supported types:
                Car
                LCV
                HGV
                Motorcycle
                Motorhome
                Caravan
                Plant
                Farm
                Other
         */
        return match (strtolower((string) $type)) {
            'lcv' => VehicleType::LCV->value,
            'motorcycle' => VehicleType::MOTORCYCLE->value,
            'motorhome' => VehicleType::MOTORHOME->value,
            'caravan' => VehicleType::CARAVAN->value,
            default => VehicleType::CAR->value,
        };
    }

    private function mapVatApplicable(?string $vatStatus): bool
    {
        // DealerKit advertises one of: 'inc-VAT', 'ex-VAT', 'No VAT'.
        // Absent/unknown defaults to true, matching the column default.
        return match (strtolower(trim((string) $vatStatus))) {
            'ex-vat', 'no vat' => false,
            default => true,
        };
    }

    private function intOrNull(mixed $value): ?int
    {
        if ($value === null || $value === '') {
            return null;
        }
        $int = (int) $value;
        return $int > 0 ? $int : null;
    }

    private function parseDate(?string $value): ?Carbon
    {
        if (empty($value)) {
            return null;
        }
        try {
            return Carbon::parse($value);
        } catch (\Exception) {
            return null;
        }
    }

    protected function getProviderName(): string
    {
        return self::PROVIDER_KEY;
    }

    protected function getDetailsForTaxonomyMap(array $record): array
    {
        return [
            'tenant_id' => tenant('id'),
            'registration_number' => $record['vehicle']['registration'] ?? null,
            'make' => $record['vehicle']['manufacturer'] ?? null,
            'model' => $record['vehicle']['model'] ?? null,
            'fuel_type' => $record['vehicle']['fuel_type'] ?? null,
            'transmission' => $record['vehicle']['transmission_type'] ?? null,
        ];
    }
}
