<?php

namespace App\Imports;

use App\Events\NewVehicleImported;
use App\Events\VehicleUpdatedFromImport;
use App\TaxonomyMap;
use App\Traits\EnsuresVehicleAttribute;
use App\Traits\ImportChecksConditions;
use App\Traits\MapsTaxonomies;
use App\VehicleType;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Event;
use Mtc\ContentManager\Models\MediaSize;
use Mtc\MercuryDataModels\Media;
use Mtc\MercuryDataModels\Vehicle;

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

    private array $vehicleAttributes;
    private array $allowed_sizes;

    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 ($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()));
    }

    public function add(array $vehicle_data): void
    {
        if ($this->isVehicleDeleted($vehicle_data)) {
            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()));
    }

    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);

        $data = array_filter([
            'was_recently_synced' => true,
            'type' => $this->getVehicleType($vehicle_data),
            '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' => $this->getMappedTaxonomy(
                TaxonomyMap::MODEL,
                $vehicle_data['model'] ?? null,
                $vehicle_data,
                $make_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,
        ], fn($value) => !is_null($value));

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

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

        // 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(
            [
                'postcode' => $vehicle_data['postcode'] ?? null,
            ],
            $vehicle
        );
    }

    protected function retrieveRequiredVehicleAttributes(): void
    {
        $this->vehicleAttributes = [
            '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;
        }

        $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']
        );

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

        $this->setUsesForModel($mediaRecords, $vehicle, ['primary' => true]);
    }

    protected function setUsesForModel(Collection $media, Vehicle $model, array $meta): void
    {
        $data = $media->map(fn($media, $index) => [
            'media_id' => $media->id,
            'owner_type' => $model->getMorphClass(),
            'owner_id' => $model->id,
            'alt_text' => $meta['alt_text'] ?? $media->getAttribute('alt_text'),
            'title' => $meta['title'] ?? $media->getAttribute('title'),
            'caption' => $meta['caption'] ?? $media->getAttribute('caption'),
            'description' => $meta['description'] ?? $media->getAttribute('description'),
            'dimensions' => $meta['dimensions'] ?? null,
            'flags' => $meta['flags'] ?? null,
            'order' => $index,
            'primary' => $index == 0 && ($meta['primary'] ?? null),
            'secondary' => $meta['secondary'] ?? null,
            'allowed_sizes' => $this->getAllowedSizes($model),
        ]);

        $model->mediaUses()->upsert($data->toArray(), ['media_id', 'owner_type', 'owner_id']);
    }

    protected function getAllowedSizes(Model $model): array
    {
        if (!isset($this->allowed_sizes)) {
            $model_dimensions = MediaSize::query()
                ->where('model', $model->getMorphClass())
                ->whereNotNull('width')
                ->whereNotNull('height')
                ->get()
                ->map(fn(MediaSize $size) => $size->width . 'x' . $size->height)
                ->toArray();

            $this->allowed_sizes = array_merge(
                config('media.thumbnail_sizes', []),
                $model_dimensions
            );
        }
        return $this->allowed_sizes;
    }

    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($description);
        }

        return null;
    }

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

    protected function isNewVehicle(array $vehicle_data): bool
    {
        $tags = $vehicle_data['tag_ids'] ?? [];
        return in_array('not:new', $tags) === false;
    }

    protected function getVehicleType(array $vehicle_data): string
    {
        $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';
    }

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