<?php

namespace App\Jobs;

use App\Events\AutoTraderIdAssignedToVehicle;
use App\Facades\Settings;
use App\Imports\AutoTraderApiToVehicleImport;
use App\IntegrationRepository;
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\Facades\Event;
use Illuminate\Support\Facades\Log;
use Mtc\MercuryDataModels\ApiNotification;
use Mtc\MercuryDataModels\Media;
use Mtc\MercuryDataModels\SalesChannelHistory;
use Mtc\MercuryDataModels\Vehicle;

class HandleAutoTraderHubNotification implements ShouldQueue
{
    use Dispatchable;
    use InteractsWithQueue;
    use Queueable;
    use SerializesModels;

    protected AutoTraderApiToVehicleImport $import;

    /**
     * Create a new job instance.
     */
    public function __construct(
        private readonly array $input,
        private ApiNotification $apiNotification
    ) {
        $this->import = resolve(AutoTraderApiToVehicleImport::class);
    }

    /**
     * Execute the job.
     */
    public function handle(): void
    {
        $action = $this->input['type'] ?? null;

        match ($action) {
            'upsert-error' => $this->handleUpsertError(),
            'stock-upsert' => $this->handleStockUpsert(),
            'STOCK_UPDATE' => $this->handleStockUpdate(),
            default => $this->handleUnrecognizedAction($action),
        };
    }

    private function handleStockUpsert(): void
    {
        if ($this->input['result'] === 'success') {
            $this->setAutoTraderIdForVehicle();
            $this->assignUploadedImages();
            $this->handleStockUpdate(false);
            $this->handleImageOrdering();
        }

        SalesChannelHistory::query()
            ->create([
                'channel' => 'auto-trader',
                'vehicle_id' => $this->input['vehicle_id'],
                'was_successful' => $this->input['result'] === 'success',
                'details' => json_encode($this->input['data']),
            ]);
    }

    private function handleUpsertError(): void
    {
        $vehicle = Vehicle::query()
            ->where('id', $this->input['vehicle_id'])
            ->withTrashed()
            ->first();

        if (!$vehicle) {
            return;
        }

        $existing = $vehicle->autoTraderData()->first();
        $previousError = $existing?->at_export_error;
        $hasDerivative = !empty($existing?->derivative_id);

        if ($existing) {
            $existing->update(['at_export_error' => $this->input['error']]);
        } else {
            $vehicle->autoTraderData()->create(array_merge(
                ['at_export_error' => $this->input['error']],
                $this->defaultPublishFlags($vehicle)
            ));
        }

        if (
            $this->input['error'] === 'missing_derivative'
            && $previousError === null
            && !$hasDerivative
        ) {
            ApplyAutoTraderDerivativeMapping::dispatch($vehicle->id);
        }
    }

    private function defaultPublishFlags(Vehicle $vehicle): array
    {
        if (Settings::get('sales-channels-auto-trader-enabled') === false) {
            return [];
        }
        return [
            'publish_advert' => $vehicle->is_new
                ? (bool) Settings::get('auto-trader-connect-publish-new-cars')
                : (bool) Settings::get('auto-trader-connect-publish-used-cars'),
            'publish_profile' => $vehicle->is_new
                ? (bool) Settings::get('auto-trader-connect-profile-new-cars')
                : (bool) Settings::get('auto-trader-connect-profile-used-cars'),
        ];
    }

    private function setAutoTraderIdForVehicle(): void
    {
        $query = Vehicle::query()
            ->where('id', $this->input['vehicle_id'])
            ->withTrashed();

        if (array_key_exists('old_stock_id', $this->input)) {
            if (is_null($this->input['old_stock_id'])) {
                $query->whereNull('auto_trader_id');
            } else {
                $query->where('auto_trader_id', $this->input['old_stock_id']);
            }
        }

        $query->update([
            'auto_trader_id' => $this->input['stock_id'],
        ]);
        /** @var Vehicle|null $vehicle */
        $vehicle = Vehicle::query()->where('auto_trader_id', $this->input['stock_id'])->first();
        if ($vehicle?->autoTraderData()->exists()) {
            $data = $this->input['data'];
            $vehicle->autoTraderData()->update([
                'at_export_error' => null,
                'advert_publish_state' => $data['adverts']['retailAdverts']['autotraderAdvert']['status'] ?? null,
                'profile_publish_state' => $data['adverts']['retailAdverts']['profileAdvert']['status'] ?? null,
                'lifecycle_state' => $data['metadata']['lifecycleState'] ?? null,
            ]);
        }
        if ($vehicle) {
            Event::dispatch(new AutoTraderIdAssignedToVehicle($vehicle));
        }
    }

    private function assignUploadedImages(): void
    {
        if (isset($this->input['uploaded_images'])) {
            collect($this->input['uploaded_images'])
                ->each(function ($image) {
                    $media = Media::query()
                        ->where('id', $image['media_id'])
                        ->first();
                    if ($media) {
                        $media->update([
                            'image_provider' => $media->image_provider ?? 'auto-trader',
                            'auto_trader_id' => $image['auto_trader_id']
                        ]);
                    }
                });
        }
    }

    private function handleStockUpdate($importImages = true): void
    {
        if (new IntegrationRepository()->isEnabled('auto-trader') === false) {
            /** @var Vehicle|null $vehicle */
            $vehicle = Vehicle::query()->where('auto_trader_id', $this->input['stock_id'])->first();
            if ($vehicle?->autoTraderData()->exists()) {
                $data = $this->input['data'];
                $vehicle->autoTraderData()->update([
                    'at_export_error' => null,
                    'advert_publish_state' => $data['adverts']['retailAdverts']['autotraderAdvert']['status'] ?? null,
                    'profile_publish_state' => $data['adverts']['retailAdverts']['profileAdvert']['status'] ?? null,
                    'lifecycle_state' => $data['metadata']['lifecycleState'] ?? null,
                ]);
            }

            // AutoTrader is not a stock provider, we do not allow creating or updating vehicles
            return;
        }
        $vehicle_data = $this->input['data'];

        if ($this->import->exists($vehicle_data, $this->input['vehicle_id'] ?? null)) {
            $this->import->update($vehicle_data, $this->input['vehicle_id'] ?? null, $importImages);
        } else {
            $this->import->add($vehicle_data, $importImages);
        }

        $this->apiNotification->processed = 1;
        $this->apiNotification->save();
    }

    private function handleUnrecognizedAction($action): void
    {
        Log::warning('AutoTrader Hub notification - unrecognized action', [
            'action' => $action,
            'input' => $this->input,
            'tenant' => tenant('id'),
        ]);
    }

    private function handleImageOrdering(): void
    {
        if (!(Settings::get('auto-trader-upsert-sort-images', false) ?? false)) {
            return;
        }

        $orderedImageIds = collect($this->input['data']['media']['images'] ?? [])
            ->pluck('imageId')
            ->filter()
            ->values();

        if ($orderedImageIds->isEmpty()) {
            return;
        }


        $mediaMap = Media::query()
            ->whereIn('auto_trader_id', $orderedImageIds)
            ->get()
            ->keyBy('auto_trader_id');


        foreach ($orderedImageIds as $order => $autoTraderImageId) {
            $media = $mediaMap->get($autoTraderImageId);

            if (!$media) {
                continue;
            }

            $media->uses()
                ->where('owner_type', 'vehicle')
                ->where('owner_id', $this->input['vehicle_id'])
                ->update([
                    'order' => $order
                ]);
        }
    }
}
