<?php

namespace App\Imports;

use App\Events\NewVehicleImported;
use App\Facades\Settings;
use App\Jobs\ImportImagesFromUrlList;
use App\Jobs\ImportImageUrlList;
use App\Master\Models\VehicleModel;
use App\Rules\ValidVehicleRegistrationNumber;
use App\TaxonomyMap;
use App\Traits\MapsTaxonomies;
use App\VehicleType;
use Carbon\Carbon;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Str;
use Mtc\AutoTraderStock\Contracts\AutoTraderApiImportContract;
use Mtc\MercuryDataModels\BodyStyleType;
use Mtc\MercuryDataModels\FuelType;
use Mtc\MercuryDataModels\TransmissionType;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleMake;

class AutoTraderApiToVehicleImport extends AutoTraderApiImportContract
{
    use DispatchesJobs;
    use MapsTaxonomies;

    private $dealershipId;
    private array $make = [];
    private array $model = [];
    private array $transmission = [];
    private array $body_style = [];
    private array $fuel_type = [];


    public function setDealershipId($dealership): void
    {
        $this->dealershipId = $dealership;
    }

    public function exists(array $vehicle_data): bool
    {
        return Vehicle::query()
            ->where(function ($query) use ($vehicle_data) {
                $query->where('uuid', $vehicle_data['metadata']['stockId'])
                    ->orWhere('auto_trader_id', $vehicle_data['metadata']['stockId'])
                    ->withTrashed();
            })
            ->orWhere(function ($query) use ($vehicle_data) {
                $query->where('registration_number', $vehicle_data['vehicle']['registration']);
            })
            ->exists();
    }

    public function update(array $vehicle_data, $importImages = true): void
    {
        /** @var Vehicle $vehicle */
        $vehicle = Vehicle::query()
            ->where(function ($query) use ($vehicle_data) {
                $query->where('uuid', $vehicle_data['metadata']['stockId'])
                    ->orWhere('auto_trader_id', $vehicle_data['metadata']['stockId']);
            })
            ->withTrashed()
            ->latest()
            ->first();

        if (!$vehicle) {
            $this->add($vehicle_data);
            return;
        }

        if ($vehicle->trashed()) {
            if ($this->isVehicleDeleted($vehicle_data)) {
                return;
            } else {
                $vehicle->restore();
            }
        }

        $vehicle->fill($this->mapDataToColumns($vehicle_data, $vehicle));

        if (!empty($vehicle_data['features'])) {
            $this->importFeatures($vehicle, $vehicle_data['features']);
        }
        if (!empty($vehicle_data['enriched_data']['features'])) {
            $this->importEquipment($vehicle, $vehicle_data['enriched_data']['features']);
        }
        $vehicle->save();

        if (Settings::get('stock-auto-trader-sync-images') && $importImages) {
            $this->importImages($vehicle, $vehicle_data['media']['images']);
        }

        $this->storeUnmappedTaxonomy($vehicle);
    }

    public function add(array $vehicle_data, $importImages = true)
    {
        if ($this->shouldSkipSync($vehicle_data)) {
            return;
        }

        /** @var Vehicle $vehicle */
        $vehicle = new Vehicle();
        $vehicle->fill($this->mapDataToColumns($vehicle_data, $vehicle, false));

        $vehicle->save();

        if (Settings::get('stock-auto-trader-sync-images') && $importImages) {
            $this->importImages($vehicle, $vehicle_data['media']['images']);
        }

        if (!empty($vehicle_data['features'])) {
            $this->importFeatures($vehicle, $vehicle_data['features']);
        }

        if (!empty($vehicle_data['enriched_data']['features'])) {
            $this->importEquipment($vehicle, $vehicle_data['enriched_data']['features']);
        }

        $this->storeUnmappedTaxonomy($vehicle);

        Event::dispatch(new NewVehicleImported($vehicle, $vehicle_data));
    }

    public function removeOld(Collection $vehicles): void
    {
        // Not used here
    }

    protected function mapDataToColumns($vehicle_data, Vehicle $vehicle, bool $existing_vehicle = true): array
    {
        if (!empty($vehicle_data['vehicle']['firstRegistrationDate'])) {
            $date_of_registration = Carbon::createFromFormat(
                'Y-m-d',
                $vehicle_data['vehicle']['firstRegistrationDate']
            );
        } else {
            $date_of_registration = null;
        }

        $price = $vehicle_data['adverts']['retailAdverts']['suppliedPrice']['amountGBP'] ?? null;
        $admin_fee = $vehicle_data['adverts']['retailAdverts']['admin_fee']['amountGBP'] ?? null;
        $rrp_price = $vehicle_data['adverts']['retailAdverts']['manufacturerRRP']['amountGBP'] ?? null;
        $make_id = $this->getMappedTaxonomy(TaxonomyMap::MAKE, $vehicle_data['vehicle']['make'], $vehicle_data);

        $data = array_filter([
            'was_recently_synced' => true,
            'type' => $this->getVehicleType($vehicle_data),
            'stock_provider' => $vehicle->stock_provider ?? $this->getProviderName(),
            'uuid' => $vehicle->uuid ?? $vehicle_data['metadata']['stockId'],
            'auto_trader_id' => $vehicle_data['metadata']['stockId'],
            'title' => $vehicle_data['vehicle']['make'] . ' ' . $vehicle_data['vehicle']['model'],
            'registration_number' => $vehicle_data['vehicle']['registration'] ?? null,
            'trim' => $vehicle_data['vehicle']['trim'] ?? null,
            'first_registration_date' => $date_of_registration,
            'make_id' => $make_id,
            'model_id' => $this->getMappedTaxonomy(
                TaxonomyMap::MODEL,
                $vehicle_data['vehicle']['standard']['model'] ?? $vehicle_data['vehicle']['model'],
                $vehicle_data,
                $make_id
            ),
            'derivative' => $vehicle_data['vehicle']['derivative'] ?? null,
            'colour' => $vehicle_data['vehicle']['standard']['colour'] ?? $vehicle_data['vehicle']['colour'] ?? null,
            'fuel_type_id' => $this->getMappedTaxonomy(
                TaxonomyMap::FUEL_TYPE,
                $vehicle_data['vehicle']['standard']['fuelType'] ?? $vehicle_data['vehicle']['fuelType'],
                $vehicle_data
            ),
            'body_style_id' => $this->getMappedTaxonomy(
                TaxonomyMap::BODY_STYLE,
                $vehicle_data['vehicle']['standard']['bodyType'] ?? $vehicle_data['vehicle']['bodyType'],
                $vehicle_data
            ),
            'engine_size_cc' => $vehicle_data['vehicle']['engineCapacityCC'] ?? null,
            'dealership_id' => $this->dealershipId,
            'rrp_price' => $rrp_price,
            'price' => $price,
            'admin_fee' => $admin_fee,
            'is_new' => $vehicle_data['vehicle']['ownershipCondition'] === 'New',
            'transmission_id' => $this->getMappedTaxonomy(
                TaxonomyMap::TRANSMISSION,
                $vehicle_data['vehicle']['standard']['transmissionType']
                    ?? $vehicle_data['vehicle']['transmissionType'],
                $vehicle_data
            ),
            'vehicle_length' => $vehicle_data['vehicle']['lengthMM'] ?? null,
            'manufacture_year' => $vehicle_data['vehicle']['yearOfManufacture'] ?? null,
            'vin' => $vehicle_data['vehicle']['vin'] ?? null,
            'odometer_mi' => $vehicle_data['vehicle']['odometerReadingMiles'] ?? null,
            'odometer_km' => !empty($vehicle_data['vehicle']['odometerReadingMiles'])
                ? (new Vehicle())->milesToKm($vehicle_data['vehicle']['odometerReadingMiles'])
                : null,
            'co2' => $vehicle_data['vehicle']['co2EmissionGPKM'] ?? null,
            'seats' => $vehicle_data['vehicle']['seats'] ?? null,
            'bhp' => $vehicle_data['vehicle']['enginePowerBHP'] ?? null,
            'battery_capacity_kwh' => $vehicle_data['vehicle']['batteryCapacityKWH'] ?? null,
            'battery_range' => $vehicle_data['vehicle']['batteryRangeMiles'] ?? null,
            'door_count' => $vehicle_data['vehicle']['doors'] ?? null,
            'previous_owner_count' => $vehicle_data['vehicle']['previousOwners'] ?? null,
            'attention_grabber' => $vehicle_data['adverts']['retailAdverts']['attentionGrabber'] ?? null,
            'description' => ($vehicle_data['adverts']['retailAdverts']['description'] ?? '') . ' '
                . ($vehicle_data['adverts']['retailAdverts']['description2'] ?? ''),

            'deleted_at' => $this->isVehicleDeleted($vehicle_data) ? Carbon::now() : null,
            'data' => array_merge($vehicle->data ?? [], $this->getAdvertFields($vehicle_data))
        ]);

        if (empty($vehicle->reserved_at) && $vehicle_data['metadata']['lifecycleState'] === 'SALE_IN_PROGRESS') {
            $data['is_reserved'] = true;
            $data['reserved_at'] = Carbon::now();
        }

        if (empty($vehicle->sold_at) && $vehicle_data['metadata']['lifecycleState'] === 'SOLD') {
            $data['is_sold'] = true;
            $data['sold_at'] = $vehicle_data['adverts']['soldDate'] ?? Carbon::now();
        }

        if (Settings::get('auto-trader-sync-is-published')) {
            $advert = $vehicle_data['adverts']['retailAdverts']['advertiserAdvert'];
            $data['is_published'] = $advert['status'] === 'PUBLISHED';
        } elseif (!$existing_vehicle) {
            $data['is_published'] = true;
        }

        if (Settings::get('stock-auto-trader-sync-video')) {
            $data['main_video_url'] = $vehicle_data['media']['video']['href'] ?? '';
        }

        if (!empty($data['registration_number'])) {
            $registrationNumberValidator = new ValidVehicleRegistrationNumber();
            $data['personalized_number_plate'] = !$registrationNumberValidator
                ->passes('registration_number', $data['registration_number']);
        }

        return $data;
    }

    protected function getAdvertFields($vehicle_data): array
    {
        return array_filter([
            'autotraderAdvert' => $vehicle_data['adverts']['retailAdverts']['autotraderAdvert'] ?? null,
            'profileAdvert' => $vehicle_data['adverts']['retailAdverts']['profileAdvert'] ?? null
        ]);
    }

    protected function importImages(Vehicle $vehicle, $image_list): void
    {
        $imageList = collect($image_list)
            ->filter(fn($image) => isset($image['href']))
            ->mapWithKeys(fn($image) => [
                str_replace('{resize}', '', $image['href']) => [
                    'description' => $image['classificationTags'][0]['label'] ?? null,
                    'caption' => $image['classificationTags'][0]['category'] ?? null,
                    'interior' => ($image['classificationTags'][0]['category'] ?? '') === 'Interior',
                    'exterior' => ($image['classificationTags'][0]['category'] ?? '') === 'Exterior',
                    'auto_trader_id' => $image['imageId'] ?? null
                ]
            ]);

        $importImagesFromUrlListJob = (new ImportImageUrlList(
            $imageList->keys(),
            $vehicle,
            [
                'imageProvider' => 'auto-trader',
                'additionalData' => $imageList->toArray()
            ]
        ));

        $this->dispatch($importImagesFromUrlListJob);
    }

    public function importFeatures(Vehicle $vehicle, array $feature_list): void
    {
        $existing = $vehicle->features()->get();
        $features = collect($feature_list)->filter(fn($feature) => $this->filterApplicableFeatures($feature));
        $to_add = [];

        foreach ($features as $feature) {
            $matched = $existing->where('category', $feature['type'])
                ->where('name', $feature['name']);
            if ($matched->isNotEmpty()) {
                $existing->forget($matched->keys());
            } else {
                $to_add[] = $feature;
            }
        }

        if ($existing->count()) {
            $vehicle->features()
                ->whereIn('id', $existing->pluck('id'))
                ->delete();
        }
        // loop through and remove ones no longer in feature list
        // then loop through to fetch ones that need to be added
        collect($to_add)
            ->each(function ($feature) use ($vehicle) {
                try {
                    $vehicle->features()
                        ->updateOrCreate([
                            'vehicle_type' => 'vehicle',
                            'slug' => Str::slug($feature['name']),
                            'category' => $feature['type'],
                        ], [
                            'name' => $feature['name'],
                        ]);
                } catch (\Exception $exception) {
                    // Handle unique constraint error
                }
            });
    }


    public function importEquipment(Vehicle $vehicle, array $equipment_list): void
    {
        $existing = $vehicle->equipment()->get();
        $equipment = collect($equipment_list)->filter(fn($feature) => $this->filterApplicableEquipment($feature));
        $to_add = [];

        foreach ($equipment as $entry) {
            $matched = $existing->where('category', $entry['type'])
                ->where('description', $entry['name'])
                ->where('type', $entry['type']);
            if ($matched->isNotEmpty()) {
                $existing->forget($matched->keys());
            } else {
                $to_add[] = $entry;
            }
        }

        if ($existing->count()) {
            $vehicle->equipment()
                ->whereIn('id', $existing->pluck('id'))
                ->delete();
        }

        collect($to_add)
            ->each(function ($entry) use ($vehicle) {
                try {
                    $vehicle->equipment()
                        ->updateOrCreate([
                            'description' => $entry['name'],
                            'type' => $entry['type'],
                            'category' => $entry['category'],
                        ], [
                            'price' => $entry['basicPrice'],
                            'vat_amount' => $entry['vatPrice'],
                        ]);
                } catch (\Exception $exception) {
                    // Handle unique constraint error
                }
            });
    }

    public function isVehicleDeleted($vehicle_data): bool
    {
        $deleted_states = [
            'DELETED',
            'WASTEBIN',
        ];

        return in_array($vehicle_data['metadata']['lifecycleState'], $deleted_states);
    }

    public function getProviderName(): string
    {
        return 'auto-trader';
    }

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

    private function shouldSkipSync(array $vehicle_data): bool
    {
        return (
                Settings::get('stock-auto-trader-sync-new-vehicles') !== true
                && $vehicle_data['vehicle']['ownershipCondition'] === 'New'
            ) || (
                Settings::get('stock-auto-trader-sync-used-vehicles') !== true
                && $vehicle_data['vehicle']['ownershipCondition'] === 'Used'
            );
    }

    private function filterApplicableEquipment($feature): bool
    {
        return match ($feature['type'] ?? '') {
            'Standard' => (bool)Settings::get('autotrader-stock-sync-standard-features'),
            'Optional' => (bool)Settings::get('autotrader-stock-sync-optional-features'),
            default => false,
        };
    }
    private function filterApplicableFeatures($feature): bool
    {
        return match ($feature['type'] ?? '') {
            'Standard' => (bool)Settings::get('autotrader-stock-sync-standard-key-features'),
            'Optional' => (bool)Settings::get('autotrader-stock-sync-optional-key-features'),
            default => false,
        };
    }

    private function getVehicleType($vehicle_data): string
    {
        return match ($vehicle_data['vehicle']['vehicleType']) {
            'Motorcycle' => VehicleType::MOTORCYCLE->value,
            'LCV' => VehicleType::LCV->value,
            default => VehicleType::CAR->value,
        };
    }

    private function eagerLoadTaxonomyMapping()
    {
        $this->body_style = BodyStyleType::query()->pluck('id', 'name')->toArray();
        $this->transmission = TransmissionType::query()->pluck('id', 'name')->toArray();
        $this->fuel_type = FuelType::query()->pluck('id', 'name')->toArray();
        $this->make = VehicleMake::query()->pluck('id', 'name')->toArray();
        $this->model = VehicleModel::query()->get()
            ->groupBy('make_id')
            ->map(fn($group) => $group->keyBy('name')->map(fn($entry) => $entry->id))
            ->toArray();
        $this->mappingLoaded = true;
    }
}
