<?php

namespace App\Modules\Stock;

use App\Contracts\ExternalMediaUrlProvider;
use App\Contracts\StockProvider;
use App\Events\StockSyncFinished;
use App\Facades\Settings;
use App\Jobs\ImportMotorsVehicleBatch;
use App\Traits\StockSyncTraits;
use App\Traits\TracksImportStats;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Mtc\ContentManager\Contracts\Media;
use Mtc\ContentManager\Models\MediaFolder;
use Mtc\MercuryDataModels\Dealership;
use Mtc\MercuryDataModels\Vehicle;

class Motors implements StockProvider, ExternalMediaUrlProvider
{
    use DispatchesJobs;
    use StockSyncTraits;
    use TracksImportStats;

    private static Collection $dealerships;

    private ?int $mediaFolderId = null;

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

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

    /**
     * Perform a scheduled import task
     *
     * @return void
     */
    public function runScheduledImport(bool $fullSync = true): void
    {
        $this->resetStats();
        Config::set('filesystems.disks.motors-sftp.username', Settings::get('stock-motors-ftp-username'));
        Config::set('filesystems.disks.motors-sftp.password', Settings::get('stock-motors-ftp-password'));

        $this->mediaFolderId = MediaFolder::query()->where('name', 'Vehicles')->first()?->id;

        Vehicle::query()
            ->where('stock_provider', $this->getProviderName())
            ->update([
                'was_recently_synced' => false,
                'pending_stock_sync' => null,
            ]);

        //        Local env testing  - switch this out for next 2 lines
        //        collect(Storage::disk('local')->files('files'))
        $files = collect(Storage::disk('motors-sftp')->files());

        $files->each(fn ($file) => Storage::disk('local')->put($file, Storage::disk('motors-sftp')->get($file)))
            ->each(fn ($file) => $this->importFile($file))
            ->each(fn ($file) => $this->cleanup($file));

        if ($files->isNotEmpty()) {
            $this->removeOld();
        }
        Event::dispatch(new StockSyncFinished('motors'));
    }

    /**
     * Fields to add to dealership management
     *
     * @return array[]
     */
    public function dealershipAdditionalDataFields(): array
    {
        return [
            'motors-location-id' => [
                'type' => 'text',
                'label' => 'Location code for Motors',
            ],
        ];
    }

    private function cleanup(string $file): void
    {
        if (app()->environment('production') || app()->runningUnitTests()) {
            Storage::disk('local')->delete("$file");
        }
    }

    public function url(Media $media, string $size): string
    {
        return "https://origin-resizer.images.autoexposure.co.uk/" . ltrim($media->src, '/');
    }

    private function importFile(string $fileName): void
    {
        $path = Storage::disk('local')->path($fileName);
        $file = fopen($path, "r");
        $batch = [];
        $count = 0;
        $headings = array_map(fn ($column) => Str::slug($column), fgetcsv($file, 200, ",", escape: '\\'));
        while (($data = fgetcsv($file, escape: '\\')) !== false) {
            $batch[] = collect($headings)
                ->flip()
                ->map(fn ($index) => $data[$index] ?? null);
            if (count($batch) == 100) {
                $count++;
                $this->collection(collect($batch), $fileName);
                $batch = [];
                dump('Batch:' . $count);
            }
        }
        fclose($file);
    }

    /**
     * @param Collection $collection
     */
    public function collection(Collection $collection, string $fileName): void
    {
        $stock = $collection->filter(fn ($vehicle) => $this->filterVehicle($vehicle));
        if ($stock->count() > 0) {
            $this->loadDealerships();
            Vehicle::query()
                ->where('stock_provider', $this->getProviderName())
                ->whereIn('uuid', $stock->pluck('vehicleid'))
                ->update(['pending_stock_sync' => 1]);

            $this->dispatch(new ImportMotorsVehicleBatch($stock, self::$dealerships, $fileName, $this->mediaFolderId));
        }
    }


    private function filterVehicle(Collection $vehicle): bool
    {
        return $this->filterByPrice($vehicle)
            && $this->filterByAge($vehicle)
            && $this->filterByMileage($vehicle)
            && $vehicle['vehicletype'] !== 'BIKE';
    }

    private function filterByPrice(Collection $vehicle): bool
    {
        $result = true;
        if (Settings::get('stock-motors-sync-with-price') === true) {
            $result = ($vehicle['price'] ?? 0) > 0;
        }
        if ($result && !empty(Settings::get('stock-motors-sync-min-price'))) {
            $result = $vehicle['price'] >= Settings::get('stock-motors-sync-min-price');
        }

        return $result;
    }

    private function filterByAge(Collection $vehicle): bool
    {
        if (empty(Settings::get('stock-motors-sync-max-age'))) {
            return true;
        }

        // No date, non-numeric or future dates
        if (empty($vehicle['regyear']) || !is_numeric($vehicle['regyear']) || ($vehicle['regyear'] > date('Y'))) {
            return false;
        }

        return $vehicle['regyear'] >= (date('Y') - Settings::get('stock-motors-sync-max-age'));
    }

    private function filterByMileage(Collection $vehicle): bool
    {
        if (empty(Settings::get('stock-motors-sync-max-mileage'))) {
            return true;
        }

        return $vehicle['mileage'] <= Settings::get('stock-motors-sync-max-mileage');
    }

    /**
     * Remove vehicles that are not part of the stock feed
     *
     * @return void
     */
    private function removeOld(): void
    {
        Vehicle::query()
            ->where('stock_provider', $this->getProviderName())
            ->where('was_recently_synced', 0)
            ->whereNull('pending_stock_sync')
            ->chunk(100, fn ($chunk) => $chunk->each(fn (Vehicle $vehicle) => $this->removeWithMedia($vehicle)));
    }


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

    private function loadDealerships(): Collection
    {
        if (!isset(self::$dealerships)) {
            self::$dealerships = Dealership::with('attributeValues')->get()
                ->each(function (Dealership $dealership) {
                    $dealership->stock_location = $dealership->additionalField('motors-location-id');
                });
        }

        return self::$dealerships;
    }
}
