<?php

namespace App\Modules\Stock\CustomEndpoint;

use App\Events\NewVehicleImported;
use App\Events\StockSyncFinished;
use App\Facades\Settings;
use App\Jobs\ImportImagesFromUrlList;
use App\TaxonomyMap;
use App\Traits\EnsuresVehicleAttribute;
use App\Traits\MapsTaxonomies;
use App\Traits\VehicleRegistrationDateFinderTrait;
use App\VehicleType;
use Carbon\Carbon;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Http;
use Mtc\MercuryDataModels\Currency;
use Mtc\MercuryDataModels\Dealership;
use Mtc\MercuryDataModels\Vehicle;
use SimpleXMLElement;

class EMCImport
{
    use DispatchesJobs;
    use MapsTaxonomies;
    use VehicleRegistrationDateFinderTrait;
    use EnsuresVehicleAttribute;

    private Collection $dealerships;
    private array $make = [];
    private array $model = [];
    private array $transmission = [];
    private array $body_style = [];
    private array $fuel_type = [];
    private array $vehicleAttributes;
    private Collection $currentStock;

    public function handle(): void
    {
        $this->retrieveRequiredVehicleAttributes();
        $this->getAllStockLocations();
        $this->import($this->fetchVehicles());
    }

    protected function getProviderName(): string
    {
        return 'emc-import';
    }

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

    /**
     * Fetch vehicles from API
     *
     * @return ?SimpleXMLElement
     */
    private function fetchVehicles(): ?SimpleXMLElement
    {
        $response = Http::get('https://stockcentral.emcgroup.co.uk/stockfeed/gforces/emc-vehicles-used.php');

        if ($response->successful()) {
            return simplexml_load_string($response->body());
        }

        return null;
    }

    /**
     * Import vehicles
     *
     * @param Collection $stock
     * @return void
     */
    private function import(SimpleXMLElement $data)
    {
        $stock = collect();
        foreach ($data as $vehicle) {
            $stock[] = $vehicle;
        }

        $this->currentStock = Vehicle::query()
            ->where('stock_provider', $this->getProviderName())
            ->withImageCount()
            ->get()
            ->keyBy('uuid');

        $stock->each(fn($vehicle) => $this->syncVehicle($vehicle));
        $this->removeOld($stock);
        Event::dispatch(new StockSyncFinished($this->getProviderName()));
    }

    /**
     * Find all stock locations to fetch data from
     * These are stored against dealerships
     *
     * @return Collection
     */
    private function getAllStockLocations(): Collection
    {
        $this->dealerships = Dealership::all()
            ->each(function (Dealership $dealership) {
                $dealership->stock_location = $dealership->data['custom-feed-location-id'] ?? null;
            });

        return $this->dealerships
            ->map(fn(Dealership $dealership) => $dealership->data['custom-feed-location-id'] ?? null)
            ->filter();
    }

    /**
     * Sync vehicle record
     *
     * @param SimpleXMLElement $data
     * @return void
     */
    private function syncVehicle(SimpleXMLElement $data): void
    {
        /** @var Vehicle $vehicle */
        $vehicle = $this->currentStock[(string)$data->identifiers->stockid] ?? new Vehicle([
            'stock_provider' => $this->getProviderName(),
            'uuid' => (string)$data->identifiers->stockid,
        ]);

        $vehicle->fill([
                'title' => $data->make . ' ' . $data->model,
                'registration_number' => (string)$data->regno,
                'first_registration_date' => (string)$data->regdate,
                'derivative' => (string)$data->trim ?? '',
                'cap_id' => (string)$data->identifiers->capid,
                'make_id' => $this->getMappedTaxonomy(TaxonomyMap::MAKE, (string)$data->make, (array)$data),
                'model_id' => $this->getMappedTaxonomy(TaxonomyMap::MODEL, (string)$data->model, (array)$data),
                'colour' => (string)$data->colour ?? null,
                'fuel_type_id' => $this->getMappedTaxonomy(TaxonomyMap::FUEL_TYPE, (string)$data->fuel, (array)$data),
                'body_style_id' => $this->getMappedTaxonomy(
                    TaxonomyMap::BODY_STYLE,
                    (string)$data->body_type,
                    (array)$data
                ),
                'type' => ($data->commercial == '1' || $data->commerical == '1')
                    ? VehicleType::LCV->value
                    : VehicleType::CAR->value,
                'dealership_id' => $this->dealershipId((int)$data->branch_id),
                'previous_owner_count' => (int)$data->owners,
                'engine_size_cc' => (int)$data->enginesize,
                'seats' => (int)$data->seats ?? null,
                'door_count' => (int)$data->doors ?? null,
                'attention_grabber' => (string)$data->descriptions->attention_grabber,
                'description' => (string)$data->description->full ?? null,
                'transmission_id' => $this->getMappedTaxonomy(
                    TaxonomyMap::TRANSMISSION,
                    (string)$data->transmission,
                    (array)$data
                ),
                'manufacture_year' => Carbon::parse((string)$data->regdate)->format('Y'),
                'main_video_url' => (string)$data->videos->video
            ]);

        $vehicle->fill([
            'is_published' => true,
            'is_new' => ((string)$data->condition) !== 'used',
            'odometer_mi' => (int)$data->mileage,
            'odometer_km' => $vehicle->milesToKm((int)$data->mileage),
            'price' => (double)$data->prices->ourprice,
            'previous_price' => (double)$data->prices['ourprice-previous'],
        ])->save();

        $this->setVehicleAttributes(
            [
                'franchise_approved' => (bool)$data->franchise_approved,
                'service_history' => (string)$data->service_history,
                'four_wheel_drive' => (string)$data->four_wheel_drive === 'Yes',
                'interior_trim' => (string)$data->interior->trim,
                'interior_colour' => (string)$data->interior->colour,
                'autotrader' => (string)$data->autotrader === 'Yes',
            ],
            $vehicle
        );

        $this->syncImages($vehicle, $data->images);

        $this->storeUnmappedTaxonomy($vehicle);

        if ($vehicle->wasRecentlyCreated) {
            Event::dispatch(new NewVehicleImported($vehicle, (array)$data));
        }
    }

    private function retrieveRequiredVehicleAttributes()
    {
        $this->vehicleAttributes = [
            'franchise_approved' => $this->getVehicleAttribute('Franchise Approved', 'boolean')->toArray(),
            'service_history' => $this->getVehicleAttribute('Service History', 'text')->toArray(),
            'four_wheel_drive' => $this->getVehicleAttribute('Has Four Wheel Drive', 'boolean')->toArray(),
            'interior_trim' => $this->getVehicleAttribute('Interior Trim', 'text')->toArray(),
            'interior_colour' => $this->getVehicleAttribute('Interior Colour', 'text')->toArray(),
            'autotrader' => $this->getVehicleAttribute('Publish To AutoTrader', 'boolean')->toArray(),
        ];
    }

    /**
     * Remove vehicles that are not part of the stock feed
     *
     * @param Collection $vehicles
     * @return void
     */
    private function removeOld(Collection $vehicles): void
    {
        Vehicle::query()
            ->where('stock_provider', $this->getProviderName())
            ->whereNotIn('uuid', $vehicles->map(fn($entry) => (int)$entry->identifiers->stockid))
            ->delete();
    }

    /**
     * Sync images for vehicle
     *
     * @param Vehicle $vehicle
     * @param SimpleXMLElement $images
     * @return void
     */
    private function syncImages(Vehicle $vehicle, SimpleXMLElement $images): void
    {
        $imageList = collect();
        foreach ($images->image as $image) {
            $imageList[] = (string)$image;
        }
        $this->dispatch(new ImportImagesFromUrlList($imageList, $vehicle, false, 'custom-endpoint', true));
    }

    /**
     * Get the dealership id from stock location
     *
     * @param $locationId
     * @return string|null
     */
    private function dealershipId($locationId): ?string
    {
        return $this->dealerships
            ->where('stock_location', $locationId)
            ->first()
            ?->id;
    }

    /**
     * handle engine sizes cc which may contain text e.g. 'litre'
     * @param bool $value
     */
    private function getEngineSize($value = false)
    {
        $value = strtoupper($value);
        $value = str_replace('LITRES', '', $value);
        $value = str_replace('LITRE', '', $value);
        $value = str_replace(' ', '', $value);

        if (is_numeric($value)) {
            return $value * 1000;
        }

        return false;
    }

    /**
     * Get currency type from value
     *
     * @param string $value
     * @return string
     */
    private function getCurrencyType(string $value = ''): string
    {
        $type = 'EUR';

        if (stripos($value, 'gbp') || stripos($value, 'pound')) {
            $type = 'GBP';
        }

        return $type;
    }

    /**
     * Get price in default currency
     *
     * @param array $data
     * @return float|int|string
     */
    private function getPrice(array $data = []): float|int|string
    {
        $price = is_numeric($data['price']) ? $data['price'] : 0;
        $priceCurrency = $this->getCurrencyType($data['currency']);

        if ($priceCurrency == 'EUR') {
            $currency = Currency::query()
                ->where('CODE', Settings::get('app-details-currency'))
                ->first();

            return $currency->ratio * $price;
        }

        if ($priceCurrency != Settings::get('app-details-currency')) {
            $priceCurrencyRate = Currency::query()
                ->where('CODE', $priceCurrency)
                ->first();

            $priceInEuro = $priceCurrencyRate->ratio * $price;

            $appCurrency = Currency::query()
                ->where('CODE', Settings::get('app-details-currency'))
                ->first();

            return $appCurrency->ratio * $priceInEuro;
        }

        return $price;
    }
}
