<?php

namespace App\Jobs;

use App\Events\StockSyncFinished;
use App\Imports\OfferImport;
use App\Imports\VehicleImport;
use App\Imports\VehicleImportWithoutHeadingRow;
use App\Models\BulkImportLog;
use App\Models\ImportMap;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Maatwebsite\Excel\Facades\Excel;
use Maatwebsite\Excel\HeadingRowImport;
use Mtc\MercuryDataModels\VehicleOffer;
use Mtc\Notifications\Facades\Notification;

class BulkImportJob implements ShouldQueue
{
    use Dispatchable;
    use InteractsWithQueue;
    use Queueable;
    use SerializesModels;

    public $timeout = 0;

    private array $additional_tasks = [];
    private ?BulkImportLog $log = null;
    private bool $remove_missing = false;

    /**
     * Create a new job instance.
     */
    public function __construct(
        private readonly string $type,
        private readonly string $pathToFile,
        private readonly string $permission,
        private readonly string $importType = '',
    ) {
        $this->onQueue('sync');
    }

    /**
     * Execute the job.
     */
    public function handle(): void
    {
        $this->trackStatus('running');
        Config::set('excel.transactions.handler', 'null');
        try {
            match ($this->type) {
                'vehicle' => $this->handleVehicleImport(),
                'offer' => $this->handleOfferImport(),
            };

            $this->trackStatus('success');
            Notification::addNotification(
                'success',
                ucfirst($this->type) . ' import finished successfully',
                [],
                $this->permission,
                $this->source(),
            );
        } catch (\Exception $exception) {
            $this->trackStatus('failed', $exception->getMessage());
            Notification::addNotification(
                'error',
                ucfirst($this->type) . ' import failed',
                [
                    $exception->getMessage(),
                    $this->pathToFile,
                ],
                $this->permission,
                $this->source(),
            );
            Log::error('Failed bulk import job ' . tenant('id'), [
                'file exists' => Storage::disk('file-storage')->exists($this->pathToFile),
                'filename' => $this->pathToFile,
                'message' => $exception->getMessage(),
                'trace' => $exception->getTrace(),
            ]);
        }
    }

    private function handleVehicleImport(): void
    {
        $import_map = false;
        if (is_numeric($this->importType)) {
            /** @var ?ImportMap $import_map */
            $import_map = ImportMap::query()->find($this->importType);
        }
        if ($import_map && $import_map->data['has_header_row'] === false) {
            $import = new VehicleImportWithoutHeadingRow(importType: $this->importType)
                ->setImportMap($import_map)
                ->setAdditionalTasks($this->additional_tasks);
            $headings = new HeadingRowImport()->toArray($this->pathToFile, 'file-storage')[0][0];
            $import->validateHeadings(array_keys($headings));
            Excel::import($import, $this->pathToFile, 'file-storage');
        } else {
            $import = new VehicleImport(importType: $this->importType)
                ->setAdditionalTasks($this->additional_tasks);
            if ($import_map) {
                $import = $import->setImportMap($import_map);
            }

            $headings = new HeadingRowImport()->toArray($this->pathToFile, 'file-storage')[0][0];
            $import->validateHeadings($headings);

            Excel::import(
                $import,
                $this->pathToFile,
                'file-storage'
            );
        }

        if ($this->remove_missing) {
            $import->removeMissingVehicles();
        }

        Event::dispatch(new StockSyncFinished(
            $import_map ? 'import-map-' . $import_map->id : $this->importType
        ));
    }

    public function setAdditionalTasks(array $tasks): self
    {
        $this->additional_tasks = $tasks;
        return $this;
    }

    public function setLog(BulkImportLog $log): self
    {
        $this->log = $log;
        return $this;
    }

    public function setRemoveMissing(bool $remove_missing): self
    {
        $this->remove_missing = $remove_missing;
        return $this;
    }

    private function handleOfferImport()
    {
        $import_started = date('Y-m-d H:i:s');

        // we need to move the file to the local disk
        Storage::disk('local')->put($this->pathToFile, Storage::disk('file-storage')->get($this->pathToFile));
        Excel::import(new OfferImport(), $this->pathToFile, 'local');
        Storage::disk('local')->delete($this->pathToFile);

        // unpublish offers that were not in the import
        VehicleOffer::query()
            ->where('updated_at', '<', $import_started)
            ->update([
                'published' => false
        ]);
    }

    private function source(): string
    {
        return match ($this->type) {
            'vehicle' => "/manage-content/vehicles/bulk-edit/",
            'offer' => "/manage-content/offers/bulk-edit/",
        };
    }

    private function trackStatus(string $status, ?string $details = null): void
    {
        if (empty($this->log)) {
            return;
        }

        $data = $this->log->data ?? [];
        $data['status'] = $status;
        if ($details) {
            $data['details'] = $details;
        }
        $this->log->update(['data' => $data]);
    }
}
