<?php

namespace App\Services;

use App\Facades\Settings;
use App\Support\FeedDescription;
use Mtc\MercuryDataModels\VehicleType;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Mtc\MercuryDataModels\ApiNotification;
use Mtc\MercuryDataModels\MediaUse;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleFeature;
use Mtc\MercuryDataModels\VehicleStandardEquipment;

class AutoTraderHub
{
    private string $token;
    private string $deleteSoldSettingKey = 'automotive-sales-channels-auto-trader-unpublish-sold';
    private string $deleteReservedSettingKey = 'automotive-sales-channels-auto-trader-unpublish-reserved';
    private string $markDeletedAsSoldSettingKey = 'auto-trader-upsert-deleted-mark-sold';
    private string $exportedFieldsSettingKey = 'auto-trader-hub-exported-optional-fields';
    private string $excludedFieldsSettingKey = 'auto-trader-hub-excluded-optional-fields';

    // Fields that Hub requires or that are system/control values — always exported regardless of tenant config.
    public const MANDATORY_EXPORT_FIELDS = [
        'id',
        'advertiser_id',
        'stock_id',
        'condition',
        'registration_number',
        'published',
        'publish_advert',
        'publish_profile',
        'vin',
        'derivative',
        'odometer_mi',
        'type',
        'price',
        'manufacture_year',
        'first_registration',
        'at_disabled',
        'autotraderAdvert',
        'profileAdvert',
        'lifecycleState',
        'actions',
    ];

    // Optional fields a tenant can include (whitelist) or exclude. value => human label (used to build setting choices).
    // Every key produced by mapVehicle() must be in MANDATORY_EXPORT_FIELDS or here (see AutoTraderHubTest guard test).
    public const OPTIONAL_EXPORT_FIELDS = [
        'make' => 'Make',
        'model' => 'Model',
        'derivative_id' => 'Derivative ID',
        'trim' => 'Trim',
        'body_type' => 'Body Type',
        'fuel_type' => 'Fuel Type',
        'transmission_type' => 'Transmission Type',
        'drivetrain' => 'Drivetrain',
        'seats' => 'Seats',
        'doors' => 'Doors',
        'colour' => 'Colour',
        'co2' => 'CO2 Emissions',
        'vehicle_length' => 'Vehicle Length',
        'engine_size_cc' => 'Engine Size',
        'battery_capacity_kwh' => 'Battery Capacity',
        'battery_range' => 'Battery Range',
        'previous_owner_count' => 'Previous Owner Count',
        'rrp_price' => 'RRP Price',
        'admin_fee' => 'Admin Fee',
        'features' => 'Features',
        'images' => 'Images',
        'video_url' => 'Video URL',
        'exDemo' => 'Ex-Demo Flag',
        'vat_scheme' => 'VAT Scheme',
        'description' => 'Description',
        'attention_grabber' => 'Attention Grabber',
        'vat_status' => 'VAT Status',
    ];

    public function setToken(?string $token): self
    {
        if ($token) {
            $this->token = $token;
        }
        return $this;
    }

    public function initExport(): string
    {
        $response = Http::withHeaders(['authorization' => $this->getToken(), 'accept' => 'application/json'])
            ->timeout(10)
            ->post($this->endpoint("bulk-export"));

        if ($response->failed()) {
            Log::error('failed to export to AutoTraderHub', [
                tenant('id'),
                $response->body(),
            ]);
            throw new \Exception($response->body());
        }

        return $response->json('reference');
    }

    public function bulkExport(string $reference, Collection $vehicles): bool
    {
        $response = Http::withHeaders(['authorization' => $this->getToken(), 'accept' => 'application/json'])
            ->timeout(10)
            ->post($this->endpoint('bulk-export/' . $reference), [
                'vehicles' => $vehicles->map(fn (Vehicle $vehicle) => $this->mapVehicle($vehicle)),
            ]);

        return $response->successful();
    }

    public function export(Vehicle $vehicle): bool
    {
        $response = Http::withHeaders([
            'authorization' => $this->getToken(),
            'accept' => 'application/json'
        ])
            ->timeout(30)
            ->post($this->endpoint('send-vehicle'), [
                'vehicle' => $this->mapVehicle($vehicle),
            ]);

        if ($response->status() > 201) {
            ApiNotification::query()->create([
                'provider' => 'at-hub',
                'data' => $response->json(),
                'data_model' => 'vehicle',
                'reference' => $vehicle->vrm_condensed,
            ]);
        }

        return $response->successful();
    }

    public function removeVehicle(Vehicle $vehicle): bool
    {
        $response = Http::withHeaders([
            'authorization' => $this->getToken(),
            'accept' => 'application/json'
        ])
            ->timeout(30)
            ->post($this->endpoint('remove-vehicle'), [
                'id' => $vehicle->id,
                'autotrader_id' => $vehicle->auto_trader_id,
            ]);

        if ($response->status() > 201 || empty($response->json('status'))) {
            ApiNotification::query()->create([
                'provider' => 'at-hub',
                'data' => $response->json(),
                'data_model' => 'vehicle',
                'reference' => $vehicle->vrm_condensed,
            ]);
        }

        return !empty($response->json('status'));
    }

    private function endpoint(string $path): string
    {
        return config('services.auto-trader-hub.url') . '/api/' . ltrim($path, '/');
    }

    private function mapVehicle(Vehicle $vehicle): array
    {
        $payload = [
            'id' => $vehicle->id,
            // We do not want to use fallback to Settings to ensure vehicles go to the correct location
            'advertiser_id' => $vehicle->dealership->data['auto-trader-location-id'] ?? null,
            'stock_id' => $vehicle->auto_trader_id,
            'condition' => $vehicle->is_new ? 'New' : 'Used',
            'registration_number' => $vehicle->registration_number,
            'description' => FeedDescription::for($vehicle),
            'attention_grabber' => Str::limit($vehicle->attention_grabber ?? '', 27),
            'published' => $vehicle->is_published,
            'publish_advert' => $vehicle->autoTraderData?->publish_advert ?? $vehicle->is_published,
            'publish_profile' => $vehicle->autoTraderData?->publish_profile ?? $vehicle->is_published,
            'vin' => $vehicle->vin,
            'make' => $vehicle->make?->name,
            'model' => $vehicle->model?->name,
            'derivative' => $vehicle->derivative,
            'derivative_id' => $vehicle->autoTraderData?->derivative_id,
            'odometer_mi' => $vehicle->odometer_mi ?? 0,
            'type' => $this->getVehicleType($vehicle),
            'trim' => $vehicle->trim,
            'body_type' => $vehicle->bodyStyle?->name,
            'fuel_type' => $vehicle->fuelType?->name,
            'transmission_type' => $vehicle->transmission?->name,
            'drivetrain' => $vehicle->drivetrain?->name,
            'seats' => $vehicle->seats,
            'price' => $vehicle->price ?? 0,
            'rrp_price' => $vehicle->rrp_price,
            'admin_fee' => $vehicle->admin_fee,
            'doors' => $vehicle->door_count,
            'co2' => $vehicle->co2,
            'colour' => $vehicle->colour,
            'vehicle_length' => $vehicle->vehicle_length,
            'manufacture_year' => $vehicle->manufacture_year,
            'first_registration' => $vehicle->first_registration_date,
            'previous_owner_count' => $vehicle->previous_owner_count,
            'engine_size_cc' => $vehicle->engine_size_cc,
            'battery_capacity_kwh' => $vehicle->battery_capacity_kwh,
            'battery_range' => $vehicle->battery_range,
            'features' => $this->getVehicleFeatures($vehicle),
            'images' => $vehicle->mediaUses->map(fn (MediaUse $mediaUse) => [
                'media_id' => $mediaUse->media->id,
                'auto_trader_id' => $mediaUse->media->auto_trader_id,
                'url' => $mediaUse->media->getOriginalUrlAttribute(true),
            ]),
            'video_url' => $vehicle->main_video_url,
            'at_disabled' => $this->getVehicleDisabledCondition($vehicle),
            'autotraderAdvert' => $vehicle->data['autotraderAdvert'] ?? null,
            'profileAdvert' => $vehicle->data['profileAdvert'] ?? null,
            'lifecycleState' => $this->getLifecycleState($vehicle),
            'actions' => [
                'sortImages' => $this->shouldSortImages($vehicle),
            ],
            'exDemo' => $vehicle->is_demo,
            'vat_scheme' => $vehicle->is_vat_qualifying ? "VAT Qualifying" : "Marginal",
        ];

        if ($vehicle->is_vat_qualifying) {
            $payload['vat_status'] = $vehicle->is_vat_applicable ? "Inc VAT" : "Ex VAT";
        }

        return $this->applyExportedFieldsSetting($payload);
    }

    /**
     * Narrow the payload according to the tenant's field settings.
     *
     * The two settings are alternate modes (mutually exclusive): if the include
     * whitelist is set it wins and the exclude list is ignored. Otherwise, if an
     * exclude list is set, every field except those (and the mandatory ones) is
     * sent. With neither configured, the full payload is exported.
     */
    private function applyExportedFieldsSetting(array $payload): array
    {
        $included = Settings::get($this->exportedFieldsSettingKey);

        if (is_array($included) && $included !== []) {
            $allowed = array_merge(self::MANDATORY_EXPORT_FIELDS, $included);

            return array_intersect_key($payload, array_flip($allowed));
        }

        $excluded = Settings::get($this->excludedFieldsSettingKey);

        if (is_array($excluded) && $excluded !== []) {
            // Mandatory fields are required by Hub and can never be excluded.
            $removable = array_diff($excluded, self::MANDATORY_EXPORT_FIELDS);

            return array_diff_key($payload, array_flip($removable));
        }

        return $payload;
    }

    private function getVehicleFeatures(Vehicle $vehicle): array
    {
        $equipmentData = $vehicle->equipment->map(fn (VehicleStandardEquipment $equipment) => [
            'name' => $equipment->description,
            'type' => $equipment->type,
            'category' => $equipment->category,
        ])->toArray();

        $featuresData = $vehicle->features->map(fn (VehicleFeature $feature) => [
            'name' => $feature->name,
            'type' => $feature->category,
        ])->toArray();

        return array_merge($equipmentData, $featuresData);
    }

    private function getLifecycleState(Vehicle $vehicle): string
    {
        if ($vehicle->deleted_at) {
            if (Settings::get($this->markDeletedAsSoldSettingKey, false) ?? false) {
                return 'SOLD';
            }
            return 'DELETED';
        }

        if (
            ((Settings::get($this->deleteSoldSettingKey, false) ?? false) && $vehicle->is_sold) ||
            ((Settings::get($this->deleteReservedSettingKey, false) ?? false) && $vehicle->is_reserved)
        ) {
            return 'DELETED';
        }

        if ($vehicle->is_sold) {
            return 'SOLD';
        }

        if ($vehicle->is_reserved) {
            return 'SALE_IN_PROGRESS';
        }

        return 'FORECOURT';
    }

    private function shouldGenerateDescription(Vehicle $vehicle): bool
    {
        return (Settings::get('auto-trader-upsert-generate-description', false) ?? false)
            && (empty($vehicle->description) || strlen($vehicle->description) > 4000);
    }

    private function shouldSortImages(Vehicle $vehicle): bool
    {
        return Settings::get('auto-trader-upsert-sort-images', false) ?? false;
    }

    private function getToken(): string
    {
        return $this->token;
    }

    public function createSite()
    {
        $response = Http::timeout(5)
            ->withHeaders([
                'X-Auth' => config('services.auto-trader-hub.api_key')
            ])
            ->post($this->endpoint('create-site'), [
                'advertiser_id' => Settings::get('auto-trader-advertiser-id'),
                'name' => tenant()->name,
                'base_url' => config('app.url'),
                'endpoint' => route('tenant.notifications.auto-trader-hub.store', tenant(), false),
                'external_id' => tenant()->id,
            ]);

        if ($response->successful()) {
            $apiToken = $response->json('api_token');
            if ($apiToken) {
                Settings::update('auto-trader-hub-token', $apiToken);
            }
        }
    }

    public function addDealerIds(array $advertiserIds): bool
    {
        $advertiserIds = array_values(array_unique(array_filter(array_map(
            fn ($id) => is_scalar($id) ? trim((string) $id) : '',
            $advertiserIds
        ))));

        if (empty($advertiserIds)) {
            return false;
        }

        $response = Http::timeout(5)
            ->withHeaders([
                'X-Auth' => config('services.auto-trader-hub.api_key'),
                'accept' => 'application/json',
            ])
            ->post($this->endpoint('add-dealer-ids'), [
                'external_id' => tenant()->id,
                'advertiser_ids' => $advertiserIds,
            ]);

        if ($response->failed()) {
            Log::error('failed to add dealer IDs to AutoTraderHub', [
                'tenant' => tenant('id'),
                'advertiser_ids' => $advertiserIds,
                'response' => $response->body(),
            ]);
        }

        return $response->successful();
    }

    private function getVehicleDisabledCondition(Vehicle $vehicle): bool
    {
        if (!$vehicle->is_published) {
            return true;
        }

        if (Settings::get('automotive-sales-channels-auto-trader-unpublish-demo') && $vehicle->is_demo) {
            return true;
        }

        if (Settings::get('automotive-sales-channels-auto-trader-unpublish-reserved') && $vehicle->is_reserved) {
            return true;
        }

        if (
            Settings::get('automotive-sales-channels-auto-trader-unpublish-no-images')
            && $vehicle->mediaUses->count() === 0
        ) {
            return true;
        }

        return false;
    }

    private function getVehicleType(Vehicle $vehicle): string
    {
        if ($vehicle->type === VehicleType::CAR->value) {
            return 'Car';
        }

        if ($vehicle->type === VehicleType::MOTORCYCLE->value) {
            return 'Bike';
        }

        return 'Van';
    }
}
