<?php

namespace App\Imports;

use App\Events\NewVehicleImported;
use App\Events\VehicleUpdatedFromImport;
use App\Facades\Settings;
use App\TaxonomyMap;
use App\Traits\ImportChecksConditions;
use App\Traits\MapsTaxonomies;
use App\VehicleType;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Event;
use Maatwebsite\Excel\Concerns\ToCollection;
use Mtc\ContentManager\Facades\Media;
use Mtc\MercuryDataModels\Dealership;
use Mtc\MercuryDataModels\Vehicle;
use Mtc\MercuryDataModels\VehicleModel;
use Maatwebsite\Excel\Concerns\SkipsFailures;
use Maatwebsite\Excel\Concerns\WithCustomCsvSettings;
use Illuminate\Support\Str;

class KeyloopRev8StockImport implements ToCollection, WithCustomCsvSettings
{
    use MapsTaxonomies;
    use SkipsFailures;
    use ImportChecksConditions;

    private const int AVOCADO_NOT = 0;
    private const int AVOCADO_OLD = 1;
    private const int AVOCADO_NEW = 2;

    public function __construct(private readonly ?string $filename)
    {
    }

    private Collection $dealerships;

    /**
     * @param Collection $collection
     */
    public function collection(Collection $collection): void
    {
        $this->dealerships = Dealership::query()
            ->with('franchise')
            ->get()
            ->each(function (Dealership $dealership) {
                $dealership->stock_location = $dealership->data['keyloop-rev8-location-id'] ?? null;
            });

        Vehicle::query()
            ->where('stock_provider', 'rev8' . ($this->filename ? '-' . $this->filename : ''))
            ->update([
                'was_recently_synced' => false
            ]);
        $stock = $collection->filter(fn ($vehicle) => $this->filterByPrice($vehicle));
        $stock->each(fn (Collection $vehicle) => $this->syncVehicle($vehicle));

        $this->removeOld($stock);
    }


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

        return ($vehicle[13]) > 0;
    }

    /**
     * Sync vehicle record
     *
     * @param array $data
     * @return void
     */
    private function syncVehicle(Collection $data): void
    {
        $registrationDate = $arrivalDate = null;
        if (!empty($data[KeyloopImportFields::REGISTRATION_DATE] > 0)) {
            if (is_numeric($data[KeyloopImportFields::REGISTRATION_DATE])) {
                // Date offset: https://www.epochconverter.com/seconds-days-since-y0
                $registrationDate = Carbon::create(1900)->addDays(
                    $data[KeyloopImportFields::REGISTRATION_DATE]
                )->subDays(2);
            } else {
                try {
                    $registrationDate = Carbon::createFromFormat(
                        'd/m/Y',
                        $data[KeyloopImportFields::REGISTRATION_DATE]
                    );
                } catch (\Exception) {
                    //
                }
            }
        }
        if (!empty($data[KeyloopImportFields::ARRIVAL_DATE])) {
            if (is_numeric($data[KeyloopImportFields::ARRIVAL_DATE])) {
                // Date offset: https://www.epochconverter.com/seconds-days-since-y0
                $arrivalDate = Carbon::create(1900)->addDays($data[KeyloopImportFields::ARRIVAL_DATE])
                    ->subDays(2);
            } else {
                try {
                    $arrivalDate = Carbon::createFromFormat('d/m/Y', $data[KeyloopImportFields::ARRIVAL_DATE]);
                } catch (\Exception) {
                    //
                }
            }
        }
        /** @var Vehicle $vehicle */
        // First we try to lookup by stock ID match for older records
        // If not found we go by stock id + vrm
        // The second match is much safer as we encounter issue where VRM/Make/Model changes but stock ID does not
        // This change is problematic with outgoing feeds
        $vehicle = Vehicle::query()
            ->where([
                'stock_provider' => 'rev8' . ($this->filename ? '-' . $this->filename : ''),
                'uuid' => trim((string) $data[KeyloopImportFields::STOCK_ID]),
            ])
            ->first();

        if (!$vehicle) {
            $vehicle = Vehicle::query()
                ->firstOrNew([
                    'stock_provider' => 'rev8' . ($this->filename ? '-' . $this->filename : ''),
                    'uuid' => trim((string) $data[KeyloopImportFields::STOCK_ID]) . '-'
                        . trim((string) $data[KeyloopImportFields::VRM]),
                ]);
        }

        $makeId = null;
        if ($data[KeyloopImportFields::MAKE] != 'Other') {
            $makeId = $this->getMappedTaxonomy(
                TaxonomyMap::MAKE,
                $data[KeyloopImportFields::MAKE],
                $data->toArray()
            );
        }

        $modelId = null;
        if (!in_array($data[KeyloopImportFields::MODEL], ['Non Franchise', 'NON FRANCHISE MODEL'])) {
            $model = $data[KeyloopImportFields::MODEL];
            $modelId = $this->getMappedTaxonomy(TaxonomyMap::MODEL, $model, $data->toArray());
        }

        $isPublished = in_array($data[KeyloopImportFields::AVAILABILITY], ['A', 'R']);
        if ($data[KeyloopImportFields::MODEL] === 'Demonstrator' && $data[KeyloopImportFields::AVAILABILITY] === 'R') {
            $isPublished = false;
        }

        $type = $data[KeyloopImportFields::DEALERSHIP] == 'MB Poole CV'
            ? VehicleType::LCV->value : VehicleType::CAR->value;
        $vat_applicable = in_array($data[KeyloopImportFields::VAT_APPLICABLE], ['Y', 'G'])
            && ($type ?? 'car') == VehicleType::CAR->value;
        // update all
        $vehicle->fill([
            'was_recently_synced' => true,
            'is_published' => $isPublished,
            'is_reserved' => $data[KeyloopImportFields::AVAILABILITY] === 'R',
            'price' => $vat_applicable
                ? trim((string) $data[KeyloopImportFields::PRICE]) * 1.2
                : trim((string) $data[KeyloopImportFields::PRICE]),
            'first_registration_date' => $registrationDate,
            'manufacture_year' => $registrationDate?->year ?? $data[KeyloopImportFields::YEAR],
            'stock_arrival_date' => $arrivalDate,
            'dealership_id' => $this->dealershipId(
                $data[KeyloopImportFields::DEALERSHIP],
                $this->isAvocadoStock($data)
            ),
            'body_style_id' => $this->getMappedTaxonomy(
                TaxonomyMap::BODY_STYLE,
                $data[KeyloopImportFields::BODY_STYLE],
                $data->toArray()
            ),
            'attention_grabber' => $data[KeyloopImportFields::ATTENTION_GRABBER] ?? null,
            'registration_number' => $data[KeyloopImportFields::VRM],
            'odometer_mi' => $data[keyloopImportFields::ODOMETER],
            'is_new' => $data[KeyloopImportFields::IS_NEW] === 'YES',
            'is_demo' => $data[KeyloopImportFields::IS_DEMO] === 'YES',
            'is_vat_applicable' => $data[KeyloopImportFields::VAT_APPLICABLE] !== 'N',
            'type' => $type
        ]);

        $vehicle->is_published = $this->shouldBePublished(
            fn () => $vehicle->is_published,
            'keyloop-rev8',
            $vehicle,
        );
        $vrm_changed = $vehicle->isDirty('registration_number');
        if ($vrm_changed) {
            $vehicle->cap_id = null;
        }

        if (!empty($modelId)) {
            $vehicle->model_id = $modelId;
        }

        // only new
        if ($vehicle->exists === false) {
            $vehicle->fill([
                'colour' => ucwords(strtolower((string) $data[KeyloopImportFields::COLOUR])),
                'fuel_type_id' => $this->getMappedTaxonomy(
                    TaxonomyMap::FUEL_TYPE,
                    $data[KeyloopImportFields::FUEL_TYPE],
                    $data->toArray()
                ),
                'door_count' => $data[KeyloopImportFields::DOORS],
                'make_id' => $makeId,
                'model_id' => $modelId,
                'derivative' => $data[KeyloopImportFields::TITLE],
                'title' => $data[KeyloopImportFields::TITLE],
                'engine_size_cc' => $data[KeyloopImportFields::ENGINE_CC],
                'transmission_id' => $this->getMappedTaxonomy(
                    TaxonomyMap::TRANSMISSION,
                    $data[KeyloopImportFields::TRANSMISSION],
                    $data->toArray()
                ),
                'vin' => $data[KeyloopImportFields::VIN],
                'attention_grabber' => $data[KeyloopImportFields::ATTENTION_GRABBER] ?? null,
            ]);
        }
        $vehicle->save();

        $this->updateImages($vehicle);
        $this->setAutoTraderPublishFlags($vehicle, $data);
        $this->storeUnmappedTaxonomy($vehicle);

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

    /**
     * Remove vehicles that are not part of the stock feed
     *
     * @param Collection $vehicles
     * @return void
     */
    private function removeOld(Collection $vehicles)
    {
        Vehicle::query()
            ->where('stock_provider', 'rev8' . ($this->filename ? '-' . $this->filename : ''))
            ->where('was_recently_synced', false)
            ->delete();
    }

    private function isAvocadoStock($data)
    {
        $avocado_stock = self::AVOCADO_NOT;

        if (
            $this->isAvocadoByDealership(
                $data[KeyloopImportFields::AVAILABILITY],
                $data[KeyloopImportFields::DEALERSHIP]
            )
        ) {
            $avocado_stock = self::AVOCADO_NEW;
        } elseif (
            $this->isAvocadoByModel(
                $data[KeyloopImportFields::AVAILABILITY],
                $data[KeyloopImportFields::MODEL]
            )
        ) {
            $avocado_stock = self::AVOCADO_OLD;
        }

        return $avocado_stock;
    }

    private function isAvocadoByDealership($availability, $dealership)
    {
        return $availability == 'A' && in_array('avocado', explode(' ', strtolower((string) $dealership)));
    }

    private function isAvocadoByModel($availability, $model)
    {
        return $availability == 'A' && in_array(trim((string) $model), ['NON FRANCHISE MODEL', 'Non Franchise']);
    }

    /**
     * Get the dealership id from stock location
     *
     * @param $locationId
     * @return string|null
     */
    private function dealershipId($locationId, $isAvocado = self::AVOCADO_NOT): ?string
    {
        $filteredDealerships = $this->dealerships->filter(function ($dealership) use ($isAvocado) {
            if ($isAvocado === self::AVOCADO_OLD || $isAvocado === self::AVOCADO_NEW) {
                return $dealership->franchise?->slug === 'avocado';
            } else {
                return $dealership->franchise?->slug !== 'avocado';
            }
        });

        // New avocado way: If it's determined to be avocado by dealership, then dealership no longer matches
        // stock_location format for avocado will be 'Avocado <Dealership Location>', e.g. Avocado Newbury, filtered
        // dealerships will already be avocado ones only where appropriate, so math dealership location in
        // stock_location rather than exact match.
        if ($isAvocado === self::AVOCADO_NEW) {
            $location_parts = explode(' ', (string) $locationId);
            $avocado_location = end($location_parts);

            return  $filteredDealerships
                ->filter(fn ($dealership) => Str::contains(
                    strtolower((string) $dealership->stock_location),
                    strtolower($avocado_location)
                ))
                ->first()
                ?->id;
        }

        // Old non-avocado & avocado way: exact match dealership to avocado or normal dealerships in stock_location as
        // previously filtered
        return $filteredDealerships->where('stock_location', $locationId)
            ->first()
            ?->id;
    }

    protected function getProviderName(): string
    {
        return 'keyloop-rev8';
    }

    private function updateTaxonomyModel($modelId, $value): void
    {
        $model = VehicleModel::find($modelId);
        if (!empty($model) && $model->name != $value) {
            $model->name = $value;
            $model->save();
        }
    }

    public function getCsvSettings(): array
    {
        return [
            'input_encoding' => 'CP1252'
        ];
    }

    private function updateImages($vehicle): void
    {
        $vehicle->loadCount('mediaUses');

        if ($vehicle->media_uses_count > 0) {
            return;
        }

        $oldVehicle = Vehicle::query()
            ->onlyTrashed()
            ->withCount('mediaUses')
            ->with('mediaUses')
            ->when(!empty($vehicle->vin), fn ($query) => $query->where('vin', $vehicle->vin))
            ->when(
                empty($vehicle->vin) && !empty($vehicle->registration_number),
                fn ($query) => $query->where('registration_number', $vehicle->registration_number)
            )
            ->first();

        if ($oldVehicle && $oldVehicle->media_uses_count > 0) {
            Media::setUsesForModel(
                $oldVehicle->mediaUses->pluck('media_id')->toArray(),
                $vehicle,
                ['primary' => true]
            );
        }
    }

    private function setAutoTraderPublishFlags(Vehicle $vehicle, array|Collection $data): void
    {
        // Do not allow publishing if price is below 1000 or is a new vehicle
        // We do not want to publish a fresh vehicle to allow draft to be created
        // and populated with data like description
        $publish_status = !($vehicle->wasRecentlyCreated || $vehicle->price < 1000);

        $vehicle->autoTraderData()->updateOrCreate([
            'vehicle_id' => $vehicle->id
        ], [
            'publish_advert' => $publish_status,
            'publish_profile' => $publish_status,
        ]);
    }
}
