<?php

namespace App\Http\Resources;

use App\Facades\Settings;
use App\Http\Resources\Traits\FormatsAutoTraderData;
use App\Http\Resources\Traits\TransformsImageAttributesForDisplay;
use App\Http\Resources\VehicleAction\FinanceFactory;
use App\Traits\HasPlaceholderImage;
use App\VehicleRepository;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\App;
use Mtc\MercuryDataModels\CatalogOffer;
use Mtc\MercuryDataModels\Events\LoadVehicleDataFields;
use Mtc\MercuryDataModels\Traits\HasVehicleCustomAttributes;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleStandardEquipment;
use Mtc\MercuryDataModels\Jobs\TrackResourceView;

class VehicleList extends JsonResource
{
    use FranchiseData;
    use FormatsAutoTraderData;
    use HasPlaceholderImage;
    use HasVehicleCustomAttributes;
    use FormatsLabelsForDisplay;
    use TransformsImageAttributesForDisplay;

    private Collection $placeholder;

    private array $srpFeaturedVehicles = [];

    public function __construct(
        public $resource,
        private readonly array $catalogOffers = [],
        private readonly bool $include_cross_sell = false,
        array $srpFeaturedVehicles = []
    ) {
        parent::__construct($resource);

        $this->srpFeaturedVehicles = $srpFeaturedVehicles;
    }

    /**
     * Transform the resource collection into an array.
     *
     * @param \Illuminate\Http\Request $request
     * @return array
     */
    #[\Override]
    public function toArray($request)
    {
        $this->trackViews();
        if ($this->resource instanceof LengthAwarePaginator) {
            $this->resource
                ->through(fn($vehicle) => $this->mapVehicle($vehicle));
        } else {
            $this->resource = $this->resource
                ->map(fn($vehicle) => $this->mapVehicle($vehicle));
        }

        $resource = $this->resource->toArray();

        $featuredCount = count($this->srpFeaturedVehicles);
        foreach ($this->srpFeaturedVehicles as $index => $vehicle) {
            $resource = $this->injectSrpFeaturedVehicle($resource, $vehicle, $index);
        }

        // Featured vehicles are excluded from the main paginated query to prevent
        // duplication, so we correct the pagination metadata here.
        if ($featuredCount > 0 && isset($resource['total'])) {
            $resource['total'] += $featuredCount;
            $resource['per_page'] += $featuredCount;
            $resource['last_page'] = (int) ceil($resource['total'] / $resource['per_page']);

            if (!empty($resource['data'])) {
                $currentPage = $resource['current_page'] ?? 1;
                $resource['from'] = ($currentPage - 1) * $resource['per_page'] + 1;
                $resource['to'] = $resource['from'] + count($resource['data']) - 1;
            }
        }

        $usedPositions = [];
        foreach ($this->catalogOffers as $catalogOffer) {
            $resource = $this->addCatalogOffer($resource, $catalogOffer, $usedPositions);
        }

        if ($this->include_cross_sell) {
            $resource = $this->addCrossSell($resource);
        }

        return $resource;
    }

    public function mapVehicle(Vehicle $vehicle)
    {
        $data = [
            'id' => $vehicle->id,
            'title' => $vehicle->title,
            'derivative' => $vehicle->derivative,
            'slug' => $vehicle->slug,
            'is_new' => $vehicle->is_new,
            'is_sold' => $vehicle->is_sold,
            'is_demo' => $vehicle->is_demo,
            'is_reserved' => $vehicle->is_reserved,
            'is_featured' => false,
            'price' => $vehicle->price,
            'make' => [
                'name' => $vehicle->make->name ?? '',
                'slug' => $vehicle->make->slug ?? '',
                'logo' => Settings::get('automotive-vehicle-brand-on-filter-card')
                    ? $vehicle->make->logo ?? ''
                    : null
            ],
            'location' => $this->locationValue($vehicle),
            'deposit' => $vehicle->deposit,
            'dealership' => $this->getDealership($vehicle),
            'fuel_type' => $vehicle->fuelType?->name,
            'body_style' => $vehicle->bodyStyle?->name,
            'transmission' => $vehicle->transmission?->name,
            'odometer_mi' => $vehicle->odometer_mi,
            'odometer_km' => $vehicle->odometer_km,
            'manufacture_year' => $vehicle->manufacture_year,
            'registration_number' => $vehicle->registration_number,
            'finance_example' => $this->getFinanceExample($vehicle),
            'model' => Settings::get('automotive-vehicle-model-on-filter-card')
                ? [
                    'name' => $vehicle->model->name ?? '',
                    'slug' => $vehicle->model->slug ?? '',
                ]
                : $vehicle->model?->name,
            'original_price' => $vehicle->original_price,
            'previous_price' => $vehicle->previous_price,
            'rrp_price' => $vehicle->rrp_price,
            'type' => $vehicle->type,
            'extra' => $this->transformImageAttributesForDisplay($this->getExtraData($vehicle)),
            'seo' => $vehicle->seo,
            'age_identifier' => $vehicle->getAgeIdentifier(),
            'custom' => $vehicle->getAllCustom(),
            'monthly_price' => $vehicle->monthly_price,
            'monthly_cost_type' => $vehicle->getMonthlyCostType(),
            'labels' => Settings::get('vehicle-labels-enabled')
                ? $this->formatLabelsForDisplay($vehicle->labels->whereIn('type', ['', 'all', 'srp'])->values())
                : [],
            'engine_size_cc' => $vehicle->engine_size_cc,
            'is_vat_applicable' => $vehicle->is_vat_applicable,
            'coming_soon' => $vehicle->getComingSoonStatus(),
            'attention_grabber' => $vehicle->attention_grabber,
            'features' => $vehicle->features->pluck('name'),
            'main_video_url' => $vehicle->main_video_url ?? '',
        ];
        if ($vehicle->relationLoaded('mediaUses')) {
            $data['images_count'] = $vehicle->mediaUses->count();
        } else {
            $data['images_count'] = $vehicle->image_count ?? 0;
        }

        if (!empty($vehicle->first_registration_date)) {
            $data['first_registration_date'] = $vehicle->first_registration_date->format('Y-m-d');
        }

        if (Settings::get('automotive-vehicle-brand-on-filter-card')) {
            $data['filter_features'] = $vehicle->featureEquipment
                ->unique(fn(VehicleStandardEquipment $equipment) => $equipment->feature_id && $equipment->filterFeature)
                ->map(fn(VehicleStandardEquipment $equipment) => [
                    'name' => $equipment->filterFeature?->name,
                    'slug' => $equipment->filterFeature?->slug,
                ])
                ->values();
        }
        if ($autoTraderData = $this->getAutoTraderData($vehicle)) {
            $data['autoTraderData'] = $autoTraderData;
        }
        if (Settings::get('vehicle-card-image-count') > 1) {
            $data['gallery'] = $this->getGallery($vehicle, Settings::get('vehicle-card-image-count'));
        } else {
            $data['thumbnail'] = [
                'small' => $vehicle->primaryMediaUse?->getUrl($this->thumbnailImageDimensions())
                    ?? $this->getPlaceholder($vehicle, $this->thumbnailImageDimensions(isPlaceholder: true)),
                'medium' => $vehicle->primaryMediaUse?->getUrl($this->mediumImageDimensions())
                    ?? $this->getPlaceholder($vehicle, $this->mediumImageDimensions(isPlaceholder: true)),
            ];
        }

        if (Settings::get('vehicle-list-load-finance-calculator')) {
            $data['finance_calculator'] = new FinanceFactory()->widget($vehicle);
            $data['finance_examples'] = new VehicleResource($vehicle)->getFinanceExamples();
        }

        $data['reservation_amount'] = $vehicle->getReservationAmount();

        if ($vehicle->is_sold && Settings::get('vehicle-hide-sold-prices')) {
            $data['price'] = null;
            $data['original_price'] = null;
            $data['previous_price'] = null;
            $data['rrp_price'] = null;
            $data['deposit'] = null;
            $data['monthly_price'] = null;
            $data['finance_example'] = null;
            if (isset($data['finance_calculator'])) {
                $data['finance_calculator'] = null;
            }
            if (isset($data['finance_examples'])) {
                $data['finance_examples'] = null;
            }
        }

        return $data;
    }

    protected function getFinanceExample(Vehicle $vehicle)
    {
        if (Settings::get('vehicle-list-load-finance-example')) {
            $type = Settings::get('vehicle-list-load-finance-example-type', 'cheapest');
            if ($type !== 'cheapest') {
                $example = $vehicle->financeExamples->where('finance_type', $type)->sortBy('monthly_price')->first();
                if ($example) {
                    $example->makeHidden([
                        'id',
                        'vehicle_id',
                        'provider',
                        'created_at',
                        'updated_at',
                    ]);
                    return $example;
                }
            }
            $vehicle->defaultFinanceExample?->makeHidden([
                'id',
                'vehicle_id',
                'provider',
                'created_at',
                'updated_at',
            ]);
            return $vehicle->defaultFinanceExample;
        }

        return null;
    }

    protected function getExtraData(Vehicle $vehicle)
    {
        return collect(event(new LoadVehicleDataFields($vehicle, 'ui')))
            ->filter()
            ->mapWithKeys(fn($field) => $field)
            ->merge($this->getCustomAttributes($vehicle));
    }

    protected function getDealership(Vehicle $vehicle)
    {
        if (empty($vehicle->dealership)) {
            return null;
        }

        $dealership = $vehicle->dealership->only([
            'contact_no',
            'email',
            'name',
            'slug',
        ]);

        if (!empty($vehicle->dealership->franchise)) {
            $franchise = $vehicle->dealership->franchise;
            $dealership['franchise'] = $franchise->only([
                'logo',
                'name',
                'slug',
            ]);
            if (!empty($franchise->primaryMediaUse)) {
                $dealership['franchise']['logo'] = $this->sizesForFranchise($franchise, $franchise->primaryMediaUse);
            }
        }

        return $dealership;
    }

    protected function locationValue(Vehicle $vehicle): string
    {
        if (tenant()->hasMultipleLocations()) {
            return $vehicle->dealership->name ?? '';
        }

        return '';
    }

    protected function trackViews(): void
    {
        if (tenant('type') === 'listing') {
            return;
        }

        $this->resource->each(
            fn(Vehicle $vehicle) => TrackResourceView::trackFilterView($vehicle)
        );
    }

    protected function largeImageDimensions(): string
    {
        return 'large';
    }

    protected function mediumImageDimensions(?bool $isPlaceholder = null): string
    {
        return $isPlaceholder
            ? config(config("automotive.placeholder_media_sizes.placeholder-image-vehicle"))['medium']
            : 'medium';
    }

    protected function thumbnailImageDimensions(?bool $isPlaceholder = null): string
    {
        return $isPlaceholder
            ? config(config("automotive.placeholder_media_sizes.placeholder-image-vehicle"))['medium']
            : 'tile';
    }

    protected function injectSrpFeaturedVehicle($resource, Vehicle $vehicle, int $position): array
    {
        $mapped = $this->mapVehicle($vehicle);
        $mapped['is_featured'] = true;

        array_splice($resource['data'], $position, 0, [$mapped]);

        return $resource;
    }

    protected function addCatalogOffer($resource, CatalogOffer $catalogOffer, array &$usedPositions = [])
    {
        $image = null;
        if ($catalogOffer->primaryMediaUse?->media) {
            $image = $catalogOffer->primaryMediaUse->media->type === 'image'
                ? $catalogOffer->getPreviewImage('325x250')
                : $catalogOffer->primaryMediaUse->media->getOriginalUrlAttribute();
        }

        $allMedia = $catalogOffer->mediaUses->map(fn($mediaUse) => [
            'id' => $mediaUse->id,
            'title' => $mediaUse->title,
            'alt_text' => $mediaUse->alt_text,
            'description' => $mediaUse->description,
            'caption' => $mediaUse->caption,
            'original' => $mediaUse->media->getOriginalUrlAttribute(),
            'sizes' => $this->allSizesForUse($catalogOffer, $mediaUse)
                ->put('original', $mediaUse->media->getOriginalUrlAttribute())
                ->toArray() ?: null,
            'type' => $mediaUse->media->type,
            'interior' => null,
            'exterior' => null,
            'hex1' => $mediaUse->media?->hex1,
            'hex2' => $mediaUse->media?->hex2,
        ]);

        $images = $allMedia->filter(fn($item) => $item['type'] !== 'video')->values();
        $videos = $allMedia->filter(fn($item) => $item['type'] === 'video')->values();

        $isPageOne = $this->resource instanceof LengthAwarePaginator
            && $this->resource->currentPage() === 1;

        $maxPosition = count($resource['data']);

        if ($catalogOffer->weight > 0 && $isPageOne) {
            $position = random_int(1, max(1, $maxPosition));

            $attempts = 0;
            while ($attempts < 10 && $this->isAdjacentToUsed($position, $usedPositions)) {
                $position = random_int(1, max(1, $maxPosition));
                $attempts++;
            }
        } else {
            $position = random_int(0, $maxPosition);
        }

        $usedPositions[] = $position;

        array_splice(
            $resource['data'],
            $position,
            0,
            [
                [
                    'id' => $catalogOffer->id,
                    'title' => $catalogOffer->name,
                    'subtitle' => $catalogOffer->subtitle,
                    'image' => $image,
                    'images' => $images->isNotEmpty() ? $images->toArray() : null,
                    'videos' => $videos->isNotEmpty() ? $videos->toArray() : null,
                    'link' => $catalogOffer->link,
                    'button_text' => $catalogOffer->button_text,
                    'type' => 'catalog-offer',
                ]
            ]
        );

        return $resource;
    }

    private function isAdjacentToUsed(int $position, array $usedPositions): bool
    {
        foreach ($usedPositions as $used) {
            if (abs($position - $used) <= 1) {
                return true;
            }
        }

        return false;
    }

    protected function addCrossSell($resource)
    {
        $crossSell = [];

        if (
            !empty($resource['data'])
            && Settings::get('vehicles-cross-sell-enabled')
            && in_array(Settings::get('vehicles-cross-sell-location', ''), ['all', 'srp'])
        ) {
            $vehicle_array = reset($resource['data']);

            $vehicle = Vehicle::query()->find($vehicle_array['id']);
            $crossSell = $vehicle
                ? [
                    'name' => __('automotive.cross-sell.similar'),
                    'items' => App::make(VehicleRepository::class)
                        ->similar(
                            $vehicle,
                            request('cross-sell-count', 3),
                            Settings::get('vehicles-cross-sell-type')
                        )
                        ->toArray(null),
                ]
                : [];
        }

        $resource['cross_sell'] = $crossSell;

        return $resource;
    }
}
