<?php

namespace App\Imports;

use App\Events\NewVehicleImported;
use App\Events\VehicleUpdatedFromImport;
use App\Facades\Feature;
use App\Facades\Settings;
use App\Master\Models\CapVehicleAutoGrabId;
use App\TaxonomyMap;
use App\Traits\EnsuresVehicleAttribute;
use App\Traits\ImportChecksConditions;
use App\Traits\MapsTaxonomies;
use App\Traits\StockSyncTraits;
use App\Traits\VehicleRegistrationDateFinderTrait;
use App\VehicleType;
use Carbon\Carbon;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Support\Facades\Event;
use Mtc\ContentManager\Facades\Media as MediaFacade;
use Mtc\MercuryDataModels\Finance\FinanceServiceHelper;
use Mtc\MercuryDataModels\Media;
use Mtc\MercuryDataModels\MediaUse;
use Mtc\MercuryDataModels\Services\FinanceService;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleModel;

class AutoGrabToVehicleImport
{
    use DispatchesJobs;
    use MapsTaxonomies;
    use StockSyncTraits;
    use ImportChecksConditions;
    use EnsuresVehicleAttribute;
    use VehicleRegistrationDateFinderTrait;

    private array $vehicleAttributes;

    public function exists(array $vehicle_data): bool
    {
        $vrm = $vehicle_data['rego'] ?? null;
        $vin = $vehicle_data['vin'] ?? null;
        $autoGrabId = $vehicle_data['id'] ?? null;

        $query = Vehicle::query()->withTrashed();

        if ($autoGrabId) {
            $exists = (clone $query)
                ->where('stock_provider', $this->getProviderName())
                ->where('uuid', $autoGrabId)
                ->exists();

            if ($exists) {
                return true;
            }
        }

        if ($vrm) {
            $exists = (clone $query)
                ->where('registration_number', $vrm)
                ->exists();

            if ($exists) {
                return true;
            }
        }

        if ($vin) {
            $exists = (clone $query)
                ->where('vin', $vin)
                ->exists();

            if ($exists) {
                return true;
            }
        }

        return false;
    }

    public function update(array $vehicle_data): void
    {
        $vehicle = $this->findExistingVehicle($vehicle_data);

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

        if ($this->shouldFilterByListingSource($vehicle_data)) {
            if (!$vehicle->trashed()) {
                $this->removeWithMedia($vehicle);
            }
            return;
        }

        // Check import conditions and make sure we remove vehicle if it matches skip conditions
        $normalizedData = $this->normalizeDataForConditionCheck($vehicle_data);
        if ($this->skipImporting($this->getProviderName(), $normalizedData)) {
            if (!$vehicle->trashed()) {
                $this->removeWithMedia($vehicle);
            }
            return;
        }

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

            $vehicle->restore();
        }

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

        $vehicle->is_published = $this->shouldBePublished(
            fn() => $vehicle->is_published,
            $this->getProviderName(),
            $vehicle
        );

        $vehicle->save();
        $this->syncAdditionalData($vehicle, $vehicle_data);

        Event::dispatch(new VehicleUpdatedFromImport($vehicle, $vehicle_data, $this->getProviderName()));
        $this->checkFinanceQuotes($vehicle);
    }

    public function add(array $vehicle_data): void
    {
        if ($this->isVehicleDeleted($vehicle_data)) {
            return;
        }

        $normalizedData = $this->normalizeDataForConditionCheck($vehicle_data);
        // Skip private seller vehicles
        if ($vehicle_data['seller_type'] === 'private' || in_array("writeoff", $vehicle_data['tag_ids'])) {
            return;
        }

        if ($this->shouldFilterByListingSource($vehicle_data)) {
            return;
        }

        if ($this->skipImporting($this->getProviderName(), $normalizedData)) {
            return;
        }

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

        $vehicle->is_published = $this->shouldBePublished(
            fn() => $vehicle->is_published,
            $this->getProviderName(),
            $vehicle,
        );
        $vehicle->save();

        $this->syncAdditionalData($vehicle, $vehicle_data);

        Event::dispatch(new NewVehicleImported($vehicle, $vehicle_data, $this->getProviderName()));
        $this->checkFinanceQuotes($vehicle, true);
    }

    protected function findExistingVehicle(array $vehicle_data): ?Vehicle
    {
        $vrm = $vehicle_data['rego'] ?? null;
        $vin = $vehicle_data['vin'] ?? null;
        $autoGrabId = $vehicle_data['id'] ?? null;

        if ($autoGrabId) {
            $vehicle = Vehicle::query()
                ->withTrashed()
                ->where('stock_provider', $this->getProviderName())
                ->where('uuid', $autoGrabId)
                ->first();

            if ($vehicle) {
                return $vehicle;
            }
        }

        if ($vrm) {
            $vehicle = Vehicle::query()
                ->withTrashed()
                ->where('registration_number', $vrm)
                ->first();

            if ($vehicle) {
                return $vehicle;
            }
        }

        if ($vin) {
            $vehicle = Vehicle::query()
                ->withTrashed()
                ->where('vin', $vin)
                ->first();

            if ($vehicle) {
                return $vehicle;
            }
        }

        return null;
    }

    protected function mapDataToColumns(array $vehicle_data, Vehicle $vehicle, bool $existing_vehicle = true): array
    {
        $make_id = $this->getMappedTaxonomy(TaxonomyMap::MAKE, $vehicle_data['make'] ?? null, $vehicle_data);
        $model_id = $this->getMappedTaxonomy(
            TaxonomyMap::MODEL,
            $vehicle_data['model'] ?? null,
            $vehicle_data,
            $make_id
        );

        $data = array_filter([
            'was_recently_synced' => true,
            'type' => $this->getVehicleType($vehicle_data, $model_id),
            'stock_provider' => $vehicle->stock_provider ?? $this->getProviderName(),
            'uuid' => $vehicle_data['id'] ?? $vehicle->uuid,
            'title' => $vehicle_data['vehicle_title'] ?? '',
            'registration_number' => $vehicle_data['rego'] ?? null,
            'vin' => $vehicle_data['vin'] ?? null,
            'make_id' => $make_id,
            'model_id' => $model_id,
            'trim' => $vehicle_data['badge'] ?? null,
            'derivative' => $vehicle_data['series'] ?? null,
            'colour' => $vehicle_data['color'] ?? null,
            'fuel_type_id' => $this->getMappedTaxonomy(
                TaxonomyMap::FUEL_TYPE,
                $vehicle_data['fuel_type'] ?? null,
                $vehicle_data
            ),
            'body_style_id' => $this->getMappedTaxonomy(
                TaxonomyMap::BODY_STYLE,
                $vehicle_data['body_type'] ?? null,
                $vehicle_data
            ),
            'transmission_id' => $this->getMappedTaxonomy(
                TaxonomyMap::TRANSMISSION,
                $vehicle_data['transmission_type'] ?? null,
                $vehicle_data
            ),
            'engine_size_cc' => $vehicle_data['capacity_cc'] ?? null,
            'manufacture_year' => $vehicle_data['year'] ?? $vehicle_data['release_year'] ?? null,
            'price' => $vehicle_data['price'] ?? null,
            'rrp_price' => $vehicle_data['vehicle_rrp'] ?? null,
            'odometer_km' => $vehicle_data['kms'] ?? null,
            'odometer_mi' => !empty($vehicle_data['kms'])
                ? new Vehicle()->kmToMiles($vehicle_data['kms'])
                : null,
            'seats' => $vehicle_data['num_seats'] ?? null,
            'door_count' => $vehicle_data['num_doors'] ?? null,
            'bhp' => $this->kwToBhp($vehicle_data['power_kw'] ?? null),
            'co2' => null,
            'is_new' => $this->isNewVehicle($vehicle_data),
            'lat' => $vehicle_data['lat'] ?? null,
            'lng' => $vehicle_data['lng'] ?? null,
            'data' => $vehicle_data,
            'first_registration_date' => $this->getRegistrationDateFromRegistrationNumber($vehicle_data['rego']),
        ], fn($value) => !is_null($value));

        $capData = $this->lookupCapData($vehicle_data);
        if ($capData) {
            $data['cap_id'] = $capData['cap_id'];
            $data['cap_code'] = $capData['cap_code'];
        }

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

        if (!$existing_vehicle) {
            $data['is_published'] = true;
        }

        if (empty($data['first_registration_date'])) {
            $data['is_published'] = false;
        }

        // Set description from listing if available
        $description = $this->getDescriptionFromListings($vehicle_data);
        if ($description) {
            $data['description'] = $description;
        }

        return $data;
    }

    protected function syncAdditionalData(Vehicle $vehicle, array $vehicle_data): void
    {
        $imageUrls = $this->collectImages($vehicle_data);
        if (!empty($imageUrls)) {
            $this->syncImages($vehicle, $imageUrls);
        }

        $this->syncVehicleAttributes($vehicle, $vehicle_data);
        $this->storeUnmappedTaxonomy($vehicle);
    }

    protected function syncVehicleAttributes(Vehicle $vehicle, array $vehicle_data): void
    {
        $this->retrieveRequiredVehicleAttributes();

        $this->setVehicleAttributes(
            [
                'contact_name' => $vehicle_data['contact_name'] ?? null,
                'postcode' => $vehicle_data['postcode'] ?? null,
            ],
            $vehicle
        );
    }

    protected function retrieveRequiredVehicleAttributes(): void
    {
        $this->vehicleAttributes = [
            'contact_name' => $this->getVehicleAttribute('Contact Name', 'text')->toArray(),
            'postcode' => $this->getVehicleAttribute('Postcode', 'text')->toArray(),
        ];
    }

    protected function collectImages(array $vehicle_data): array
    {
        $images = [];

        if (!empty($vehicle_data['cover_image_url'])) {
            $images[] = $vehicle_data['cover_image_url'];
        }

        for ($i = 2; $i <= 5; $i++) {
            $key = "image_url_{$i}";
            if (!empty($vehicle_data[$key])) {
                $images[] = $vehicle_data[$key];
            }
        }

        return $images;
    }

    protected function syncImages(Vehicle $vehicle, array $imageUrls): void
    {
        if (empty($imageUrls)) {
            return;
        }

        // Create or update media records for each image URL
        $images = collect($imageUrls)
            ->map(fn($imageUrl) => [
                'src' => $imageUrl,
                'image_provider' => $this->getProviderName(),
                'type' => 'image',
                'external' => true,
                'folder_id' => null,
                'path' => '',
                'upload_date' => Carbon::now()->format('Y-m'),
            ]);

        Media::query()->upsert(
            $images->toArray(),
            ['src', 'image_provider']
        );

        // Retrieve the media IDs for the upserted records
        $mediaIds = Media::query()
            ->whereIn('src', $images->pluck('src'))
            ->where('image_provider', $this->getProviderName())
            ->pluck('id')
            ->toArray();

        // Reset primary flag on media_uses that will be removed
        $vehicle->mediaUses()
            ->whereNotIn('media_id', $mediaIds)
            ->where('primary', true)
            ->update(['primary' => false]);

        // Use the Media facade to properly create media_uses records
        // This ensures proper linking between media and vehicle
        MediaFacade::setUsesForModel($mediaIds, $vehicle, ['primary' => true], false);

        $vehicle->mediaUses()
            ->whereNotIn('media_id', $mediaIds)
            ->where('owner_type', $vehicle->getMorphClass())
            ->where('owner_id', $vehicle->id)
            ->get()
            ->each(function (MediaUse $use) {
                $mediaId = $use->media_id;
                $use->delete();

                // Only delete media record if no other media_uses reference it
                $hasOtherUses = MediaUse::query()
                    ->where('media_id', $mediaId)
                    ->exists();

                if (!$hasOtherUses) {
                    Media::query()->where('id', $mediaId)->delete();
                }
            });
    }

    protected function getDescriptionFromListings(array $vehicle_data): ?string
    {
        $listings = $vehicle_data['listings'] ?? [];
        if (empty($listings)) {
            return null;
        }

        // Get the first listing description
        $description = $listings[0]['description'] ?? null;
        if ($description) {
            // Strip HTML tags for clean text
            return strip_tags((string) $description);
        }

        return null;
    }

    protected function isVehicleDeleted(array $vehicle_data): bool
    {
        return !empty($vehicle_data['removed_at']);
    }

    protected function isNewVehicle(array $vehicle_data): bool
    {
        return ($vehicle_data['kms'] ?? 0) < 1000;
    }

    protected function getVehicleType(array $vehicle_data, ?int $model_id = null): string
    {
        // First, try to get vehicle type from the vehicle model
        if ($model_id) {
            $vehicleModel = VehicleModel::find($model_id);
            if ($vehicleModel && $vehicleModel->type) {
                return $vehicleModel->type;
            }
        }

        // Fall back to determining type from body_type
        $bodyType = strtolower($vehicle_data['body_type'] ?? '');

        if (str_contains($bodyType, 'van') || str_contains($bodyType, 'commercial')) {
            return VehicleType::LCV->value;
        }

        if (str_contains($bodyType, 'motorcycle') || str_contains($bodyType, 'bike')) {
            return VehicleType::MOTORCYCLE->value;
        }

        return VehicleType::CAR->value;
    }

    protected function kwToBhp(?int $kw): ?int
    {
        if ($kw === null) {
            return null;
        }
        return (int) round($kw * 1.34102);
    }

    public function getProviderName(): string
    {
        return 'autograb';
    }

    /**
     * Normalize AutoGrab data fields to Vehicle model field names for condition checks.
     * This allows import conditions to be configured using standard Vehicle field names.
     */
    protected function normalizeDataForConditionCheck(array $vehicle_data): array
    {
        $make_id = $this->getMappedTaxonomy(TaxonomyMap::MAKE, $vehicle_data['make'] ?? null, $vehicle_data);
        $model_id = $this->getMappedTaxonomy(
            TaxonomyMap::MODEL,
            $vehicle_data['model'] ?? null,
            $vehicle_data,
            $make_id
        );

        return [
            // Direct mappings from AutoGrab to Vehicle fields
            'registration_number' => $vehicle_data['rego'] ?? null,
            'vrm_condensed' => isset($vehicle_data['rego'])
                ? str_replace(' ', '', $vehicle_data['rego'])
                : null,
            'vin' => $vehicle_data['vin'] ?? null,
            'uuid' => $vehicle_data['id'] ?? null,
            'title' => $vehicle_data['vehicle_title'] ?? null,
            'make' => $vehicle_data['make'] ?? null,
            'model' => $vehicle_data['model'] ?? null,
            'trim' => $vehicle_data['badge'] ?? null,
            'derivative' => $vehicle_data['series'] ?? null,
            'colour' => $vehicle_data['color'] ?? null,
            'fuel_type' => $vehicle_data['fuel_type'] ?? null,
            'body_style' => $vehicle_data['body_type'] ?? null,
            'transmission' => $vehicle_data['transmission_type'] ?? null,
            'engine_size_cc' => $vehicle_data['capacity_cc'] ?? null,
            'manufacture_year' => $vehicle_data['year'] ?? $vehicle_data['release_year'] ?? null,
            'price' => $vehicle_data['price'] ?? null,
            'rrp_price' => $vehicle_data['vehicle_rrp'] ?? null,
            // AutoGrab sends "local mileage value" as kms field, meaning UK gets miles in kms field
            'odometer_mi' => $vehicle_data['kms'] ?? null,
            'odometer_km' => !empty($vehicle_data['kms'])
                ? new Vehicle()->milesToKm($vehicle_data['kms'])
                : null,
            'seats' => $vehicle_data['num_seats'] ?? null,
            'door_count' => $vehicle_data['num_doors'] ?? null,
            'bhp' => $this->kwToBhp($vehicle_data['power_kw'] ?? null),
            'is_new' => $this->isNewVehicle($vehicle_data),
            'type' => $this->getVehicleType($vehicle_data, $model_id),
        ];
    }

    protected function shouldFilterByListingSource(array $vehicle_data): bool
    {
        $allowed = Settings::get('stock-autograb-listing-sources', '');
        if (empty($allowed)) {
            return false;
        }

        $allowedSources = array_map('trim', explode(',', $allowed));
        $vehicleSources = $vehicle_data['listing_sources'] ?? [];

        return empty(array_intersect($allowedSources, $vehicleSources));
    }

    protected function lookupCapData(array $vehicle_data): ?array
    {
        if (!Feature::isEnabled('cap-database')) {
            return null;
        }

        $vehicleId = $vehicle_data['vehicle_id'] ?? null;
        if (!$vehicleId) {
            return null;
        }

        $mapping = CapVehicleAutoGrabId::query()->where('autograb_vehicle_id', $vehicleId)->first();
        if (!$mapping) {
            return null;
        }

        $capVehicle = $mapping->capVehicle;
        if (!$capVehicle || !$capVehicle->cap_id) {
            return null;
        }

        return [
            'cap_id' => $capVehicle->cap_id,
            'cap_code' => $capVehicle->cap_code,
        ];
    }

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

    private function checkFinanceQuotes(Vehicle $vehicle, bool $create = false): void
    {
        if (empty($vehicle->cap_id) || !FinanceServiceHelper::hasEnabledProvider()) {
            return;
        }

        if (!$create || $vehicle->financeExamples()->exists()) {
            return;
        }

        new FinanceService()->request($vehicle, [], true);
    }
}
