<?php

namespace App\Modules\Stock;

use App\Contracts\StockProvider;
use App\Events\NewVehicleImported;
use App\Events\StockSyncFinished;
use App\Events\VehicleUpdatedFromImport;
use App\Facades\Settings;
use App\Jobs\ImportImagesFromUrlList;
use App\TaxonomyMap;
use App\Traits\ImportChecksConditions;
use App\Traits\StockSyncTraits;
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 Illuminate\Support\Facades\Log;
use Mtc\MercuryDataModels\Dealership;
use Mtc\MercuryDataModels\Vehicle;

class Pinewood implements StockProvider
{
    use DispatchesJobs;
    use StockSyncTraits;
    use ImportChecksConditions;

    public const NAME = 'pinewood';

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

    /**
     * Check if enabled
     *
     * @return bool
     */
    public function enabled(): bool
    {
        return Settings::get('stock-pinewood-enabled', false) ?? false;
    }

    /**
     * Name of the integration
     *
     * @return string
     */
    public function name(): string
    {
        return 'Pinewood DMS';
    }

    /**
     * Perform a scheduled import task
     *
     * @return void
     */
    public function runScheduledImport(bool $fullSync = true): void
    {
        $this->import($this->fetchVehicles());
    }

    /**
     * Fields to add to dealership management
     *
     * @return array[]
     */
    public function dealershipAdditionalDataFields()
    {
        return [
            'pinewood-location-id' => [
                'type' => 'text',
                'label' => 'Location ID within Pinewood DMS',
            ],
        ];
    }

    /**
     * Get the vehicle data for a location from API
     *
     * @param string $locationId
     * @return Collection
     */
    public function fetchVehiclesFromLocation(string $locationId): Collection
    {
        $params = [
            'includeNewVehicles' => Settings::get('stock-pinewood-sync-new-vehicles') ? 'true' : 'false',
            'includeUsedVehicles' => Settings::get('stock-pinewood-sync-used-vehicles') ? 'true' : 'false',
            'includeImageDetails' => Settings::get('stock-pinewood-sync-images') ? 'true' : 'false',
            'includeVehicleWithInternetPriceOnly' => Settings::get('stock-pinewood-sync-only-with-internet-price')
                ? 'true'
                : 'false',
        ];
        $url = 'https://api.pinnacledms.net/APIv2/Vehicles/StockList?' . http_build_query($params);
        $response = Http::withHeaders([
            'Accept' => 'application/json',
            'UserName' => Settings::get('stock-pinewood-username'),
            'Password' => Settings::get('stock-pinewood-password'),
            'OrganisationalUnit_UID' => $locationId,
            'Accept-Encoding' => 'gzip,deflate',
        ])
            ->get($url);

        if ($response->successful()) {
            return collect($response->json());
        }

        Log::warning('Pinewood sync issue', [
            'tenant' => tenant('id'),
            'location' => $locationId,
            'response_code' => $response->status(),
            'response' => $response->body(),
        ]);

        return collect([]);
    }

    protected function getProviderName(): string
    {
        return 'pinewood';
    }

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

    /**
     * Fetch vehicles from API
     *
     * @return Collection
     */
    private function fetchVehicles(): Collection
    {
        return $this->getAllStockLocations()
            ->map(fn($locationId) => $this->fetchVehiclesFromLocation($locationId))
            ->flatten(1)
            ->unique('Vehicle_UID');
    }

    /**
     * Import vehicles
     *
     * @param Collection $stock
     * @return void
     */
    private function import(Collection $stock)
    {
        $stock = $stock->filter(fn ($vehicle) => $this->filterByPrice($vehicle));
        $stock->each(fn(array $vehicle) => $this->syncVehicle($vehicle));

        $this->removeOld($stock->pluck('Vehicle_UID'));
        Event::dispatch(new StockSyncFinished(self::NAME));
    }

    /**
     * Filter out vehicles depending on price settings
     *
     * @param array $vehicle
     * @return bool
     */
    private function filterByPrice(array $vehicle): bool
    {
        if (Settings::get('stock-pinewood-sync-with-price') !== true) {
            return true;
        }

        if (Settings::get('stock-pinewood-sync-only-with-internet-price')) {
            return $vehicle['InternetPrice'] > 0;
        }

        return ($vehicle['InternetPrice'] ?? $vehicle['RetailPrice']) > 0;
    }

    /**
     * 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['pinewood-location-id'] ?? null;
            });

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

    /**
     * Sync vehicle record
     *
     * @param array $data
     * @return void
     */
    private function syncVehicle(array $data): void
    {
        /** @var Vehicle $vehicle */
        $vehicle = Vehicle::query()
            ->where('uuid', $data['Vehicle_UID'])
            ->firstOrNew([
                'stock_provider' => self::NAME,
                'uuid' => $data['Vehicle_UID'],
            ], [
                'type' => VehicleType::CAR->value,
                'uuid' => $data['Vehicle_UID'],
                'title' => $data['Make']['Make'] . ' ' . $data['Model']['Model'],
                'registration_number' => $data['RegistrationNumber'],
                'first_registration_date' => $data['RegistrationDate'],
                'make_id' => $this->getMappedTaxonomy(TaxonomyMap::MAKE, $data['Make']['Make'], $data),
                'model_id' => $this->getMappedTaxonomy(TaxonomyMap::MODEL, $data['Model']['Model'], $data),
                'colour' => $data['Colour'],
                'fuel_type_id' => $this->getMappedTaxonomy(
                    TaxonomyMap::FUEL_TYPE,
                    $data['FuelType']['FuelType'],
                    $data
                ),
                'body_style_id' => $this->getMappedTaxonomy(
                    TaxonomyMap::BODY_STYLE,
                    $data['BodyStyle']['BodyStyle'],
                    $data
                ),
                'engine_size_cc' => $data['EngineSize'],
                'dealership_id' => $this->dealershipId($data['Location']),
            ]);

        $vehicle->fill([
            'price' => $data['InternetPrice'] ?? $data['RetailPrice'],
            'is_new' => $data['IsNew'],
            'dealership_id' => $this->dealershipId($data['Location']),
            'transmission_id' => $this->getMappedTaxonomy(
                TaxonomyMap::TRANSMISSION,
                $data['Transmission']['Transmission'],
                $data
            ),
            'manufacture_year' => $this->modelYear($data),
            'vin' => $data['VIN'],
            'odometer_mi' => $data['Odometer'],
            'odometer_km' => $vehicle->milesToKm($data['Odometer']),
            'previous_owner_count' => $data['PreviousOwners'],
            'co2' => $data['CO2Emission'],
        ]);

        $vehicle->is_published = $this->shouldBePublished(
            fn() => $vehicle->is_published,
            'pinewood',
            $vehicle,
        );
        $vehicle->save();

        if ($this->shouldSyncImages($vehicle, $data)) {
            $this->syncImages($vehicle, $data['Images']);
        }

        $this->storeUnmappedTaxonomy($vehicle);

        if ($vehicle->wasRecentlyCreated) {
            Event::dispatch(new NewVehicleImported($vehicle, $data, $this->getProviderName()));
        } else {
            Event::dispatch(new VehicleUpdatedFromImport($vehicle, $data, $this->getProviderName()));
        }

        if ($vehicle->wasChanged('price')) {
            $this->priceChangeEvents($vehicle, self::NAME);
        }
    }

    /**
     * Check if images should be synced for vehicles
     *
     * @param Vehicle $vehicle
     * @param array $data
     * @return bool
     */
    private function shouldSyncImages(Vehicle $vehicle, array $data): bool
    {
        return !empty($data['Images'])
            && $vehicle->mediaUses()->exists() === false;
    }

    /**
     * Sync images for vehicle
     *
     * @param Vehicle $vehicle
     * @param array $images
     * @return void
     */
    private function syncImages(Vehicle $vehicle, array $images)
    {
        $imageList = collect($images)->map(fn ($image) => rtrim($image['ImageURI'], '?'));
        $this->dispatch(new ImportImagesFromUrlList($imageList, $vehicle, false, 'pinewoood'));
    }

    /**
     * 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()
            ?->stock_location;
    }

    /**
     * Find the value of model year from the registration date field
     *
     * @param $vehicle_data
     * @return string|null
     */
    private function modelYear($vehicle_data)
    {
        try {
            $year = Carbon::parse($vehicle_data['RegistrationDate'])->format('Y');
            return $year > 1900 ? $year : null;
        } catch (\Exception $exception) {
            return null;
        }
    }
}
