<?php

namespace App\Services;

use App\Contracts\AIContentDriver;
use App\Contracts\SmartImageManagementDriver;
use App\Facades\Settings;
use Illuminate\Support\Collection;
use Mtc\MercuryDataModels\ApiNotification;
use Mtc\MercuryDataModels\Dealership;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleAutoTraderData;
use Mtc\MercuryDataModels\VehicleOffer;

class AutoTrader implements AIContentDriver, SmartImageManagementDriver
{
    public function __construct(private readonly AutoTraderApi $api, private readonly AutoTraderAIApi $AIApi)
    {
    }

    public function canGenerateDescriptionOnImport(): bool
    {
        return (bool)Settings::get('ai-content-auto-trader-generate-description-on-vehicle-import');
    }

    public function getStock(int $advertiserId)
    {
        $pageSize = 200;
        $params = [
            'advertiserId' => $advertiserId,
            'pageSize' => $pageSize,
            'responseMetrics' => 'true',
            'vehicleMetrics' => 'true',
            'valuations' => 'true',
        ];
        $data = $this->api->get('stock?' . http_build_query($params));
        if (!isset($data['totalResults'])) {
            // Bad data
            return [];
        }
        if ($data['totalResults'] > $pageSize) {
            $page = 1;
            do {
                // Keep the metrics params on follow-up pages so vehicles beyond
                // the first page do not have their metrics upserted as null
                $params['page'] = ++$page;
                $nextPage = $this->api->get('stock?' . http_build_query($params));
                $data['results'] = array_merge($nextPage['results'], $data['results']);
            } while ($page * $pageSize < $data['totalResults']);
        }

        return $data['results'];
    }

    public function getAutoTraderData(int $advertiserId): void
    {
        collect($this->getStock($advertiserId))
            ->map(fn ($result) => [
                'uuid' => $result['metadata']['externalStockId'],
                'stockId' => $result['metadata']['stockId'] ?? null,
                'registration' => $result['vehicle']['registration'] ?? null,
                'valuation' => $result['valuations']['marketAverage']['retail']['amountGBP'] ?? null,
                'price_point' => $result['adverts']['retailAdverts']['priceIndicatorRating'] ?? null,
                'price_position' => $result['vehicleMetrics']['local']['retail']['rating']['value'] ?? null,
                'search_results_7d' => $result['responseMetrics']['lastWeek']['searchViews'] ?? null,
                'ad_views' => $result['responseMetrics']['yesterday']['advertViews'] ?? null,
                'ad_views_7d' => $result['responseMetrics']['lastWeek']['advertViews'] ?? null,
                'performance_rating' => $result['responseMetrics']['performanceRating']['rating'] ?? null,
                'lead_count_rating' => $result['responseMetrics']['leadCountRating']['rating'] ?? null,
            ])
            ->chunk(50)
            ->each(function ($atData) {

                $vehicles = Vehicle::query()->whereIn('uuid', $atData->pluck('uuid'))->pluck('id', 'uuid');
                if ($vehicles->isEmpty()) {
                    $vehicles = Vehicle::query()->whereIn('uuid', $atData->pluck('stockId'))->pluck('id', 'uuid');
                }
                if ($vehicles->isEmpty()) {
                    $vehicles = Vehicle::query()->whereIn('vrm_condensed', $atData->pluck('registration'))
                        ->pluck('id', 'vrm_condensed');
                }
                $data = $atData->map(function ($data) use ($vehicles) {
                    $data['vehicle_id'] = $vehicles[$data['uuid']]
                        ?? $vehicles[$data['stockId']]
                        ?? $vehicles[$data['registration']]
                        ?? null;
                    unset($data['uuid']);
                    unset($data['registration']);
                    unset($data['stockId']);
                    return $data;
                })
                    ->filter(fn ($vehicleData) => $vehicleData['vehicle_id'])
                    ->toArray();

                VehicleAutoTraderData::query()->upsert($data, ['vehicle_id']);
            });
    }

    public function getDescription(Vehicle|VehicleOffer $model): string
    {
        if (empty($model->auto_trader_id)) {
            return '';
        }

        $response = $this->AIApi->post(
            "co-driver/stock/{$model->auto_trader_id}",
            [],
            [
                'query' => [
                    'description' => 'true',
                    'advertiserId' => $this->getVehicleAdvertiserId($model),
                ]
            ]
        );
        $this->checkAPIResponseErrors($model, $response);


        ApiNotification::query()
            ->create([
                'provider' => 'auto-trader-co-driver',
                'data' => $response,
                'processed' => true,
                'data_model' => $model->getMorphClass(),
                'reference' => is_a($model, Vehicle::class) ? $model->vrm_condensed : $model->id,
            ]);

        return $response['adverts']['retailAdverts']['description'] ?? '';
    }

    public function getSmartImageManagementData(Vehicle|VehicleOffer $model): array
    {
        if (empty($model->auto_trader_id)) {
            return [
                'error' => 'Vehicle is not linked with AutoTrader'
            ];
        }

        $model->load('mediaUses.media');
        if (!$this->allModelImagesUploaded($model)) {
            return [
                'error' => 'Not all images are synced with AutoTrader.'
            ];
        }

        $response = $this->AIApi->post(
            "co-driver/stock/{$model->auto_trader_id}",
            [],
            [
                'query' => [
                    'images' => 'true',
                    'advertiserId' => $this->getVehicleAdvertiserId($model),
                ]
            ]
        );

        return [
            'recommended_image_order' => $this->getRecommendedImageOrder($model, $response['images'] ?? []),
            'key_images' => $this->getKeyImages($response['images'] ?? []),
        ];
    }

    protected function allModelImagesUploaded(Vehicle|VehicleOffer $model): bool
    {
        return $model->mediaUses
            ->every(fn ($mediaUse) =>
                optional($mediaUse->media)->auto_trader_id !== null);
    }

    protected function getRecommendedImageOrder(
        Vehicle|VehicleOffer $model,
        array $smartImageManagementImages
    ): Collection {
        $images = collect($smartImageManagementImages);

        return $model->mediaUses->map(function ($mediaUse) use ($images) {
            $media = $mediaUse->media;

            if (!$media || !$media->auto_trader_id) {
                return $mediaUse;
            }

            $matchIndex = $images->search(
                fn ($image) => $image['imageId'] === $media->auto_trader_id
            );

            $mediaUse->order = ($matchIndex !== false)
                ? $matchIndex
                : $mediaUse->order + 100;

            return $mediaUse;
        })
            ->sortBy('order')
            ->values();
    }

    protected function getKeyImages(array $smartImageManagementImages): array
    {
        return collect($smartImageManagementImages)
            ->filter(fn ($image) => is_null($image['imageId']))
            ->map(fn ($image) => $image['classificationTags'][0]['label'])
            ->values()
            ->toArray();
    }

    protected function getVehicleAdvertiserId(Vehicle|VehicleOffer $model): int
    {
        if (!empty($model->dealership->data['auto-trader-location-id'])) {
            return $model->dealership->data['auto-trader-location-id'];
        }

        return Settings::get('auto-trader-advertiser-id');
    }

    protected function checkAPIResponseErrors(Vehicle|VehicleOffer $model, array $data): void
    {
        if (empty($data['warnings'])) {
            return;
        }

        foreach ($data['warnings'] as $warning) {
            if ($this->isWrongLocationError($warning)) {
                $this->resolveLocationError($model, $warning['message']);
            }
        }
    }

    private function isWrongLocationError(array $warning): bool
    {
        return $warning['type'] === 'ERROR'
            && str_contains((string) $warning['message'], "isn't equal to header did");
    }

    private function resolveLocationError(Vehicle|VehicleOffer $model, string $message): void
    {
        // Only update vehicles
        if (is_a($model, Vehicle::class) === false) {
            return;
        }

        if ($model->stock_provider === 'auto-trader') {
            // AT stock - vehicle has changed location, update website
            preg_match('/Stock did (\d+) isn\'t equal to header did \d+/', $message, $matches);
            $correct_id = $matches[1] ?? null;

            if (Settings::get('autotrader-make-based-assignment')) {
                // Co-located dealerships share the location id: pick the sibling by make.
                $correct_location = $correct_id === null
                    ? null
                    : app(AutoTraderDealershipResolver::class)->resolve($correct_id, $model->make_id);
            } else {
                $correct_location = Dealership::all()
                    ->filter(fn (Dealership $dealership) => $dealership->data['auto-trader-location-id'] == $correct_id)
                    ->first();
            }

            if ($correct_location) {
                $model->update([
                    'dealership_id' => $correct_location->id,
                ]);
            }
        } else {
            // Need to remove AT ID as it is wrong
            $model->update([
                'auto_trader_id' => null
            ]);
        }
    }
}
