<?php

namespace App\Console\Commands;

use App\Mail\NewVehicleMakesModelsMail;
use App\Master\Models\BodyStyleType;
use App\Master\Services\AutoTraderTaxonomyService;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use App\Master\Models\VehicleMake;
use App\Master\Models\VehicleModel;
use Symfony\Component\Console\Helper\ProgressBar;

class SyncAutoTraderTaxonomies extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'auto-trader:sync-taxonomies';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Pull Taxonomy data from AutoTrader (makes/models)';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct(protected AutoTraderTaxonomyService $service)
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $makes = collect($this->service->getMakes())
            ->filter(fn($make) => !empty($make['name']) && !Str::contains($make['name'], ["'", '+', '!', '=', '.']))
            ->map(function (array $make) {
                // exists by id and vehicle types
                $existing = VehicleMake::query()
                    ->where('autotrader_id', $make['makeId'])
                    ->where('cars', $make['cars'])
                    ->where('lcvs', $make['lcvs'])
                    ->where('motorcycles', $make['motorcycles'])
                    ->where('caravans', $make['caravans'])
                    ->where('motorhomes', $make['motorhomes'])
                    ->first();
                if ($existing) {
                    return  $existing;
                }

                // find by matching name or create new one (in case CAP has already populated)
                return VehicleMake::query()
                    ->updateOrCreate(
                        [
                            'name' => $make['name'],
                        ],
                        [
                            'autotrader_id' => $make['makeId'],
                            'cars' => $make['cars'] ?? false,
                            'lcvs' => $make['lcvs'] ?? false,
                            'motorcycles' => $make['motorcycles'] ?? false,
                            'caravans' => $make['caravans'] ?? false,
                            'motorhomes' => $make['motorhomes'] ?? false,
                        ]
                    );
            });

        $this->info($makes->count() . ' fetched');
        $allMakes = VehicleMake::query()->pluck('id', 'autotrader_id');
        $progressBar = $this->output->createProgressBar($makes->count());

        $makes->each(fn(VehicleMake $make) => $this->syncModels($make['autotrader_id'], $allMakes, $progressBar));

        $this->syncBodyStyles();

        $this->notifyOnAdditions();

        return 0;
    }

    /**
     * Sync model data for a make
     *
     * @param $makeId
     * @param $all_makes
     */
    protected function syncModels($makeId, Collection $allMakes, ProgressBar $progressBar)
    {
        $progressBar->advance();
        if (empty($allMakes[$makeId])) {
            return;
        }

        collect($this->service->getModels($makeId, 'Car'))
            ->filter(fn($model) => !empty($model['name']))
            ->each(fn(array $model) => $this->processModelEntry($model, 'car', $makeId, $allMakes));

        collect($this->service->getModels($makeId, 'Van'))
            ->filter(fn($model) => !empty($model['name']))
            ->each(fn(array $model) => $this->processModelEntry($model, 'lcv', $makeId, $allMakes));

        collect($this->service->getModels($makeId, 'Bike'))
            ->filter(fn($model) => !empty($model['name']))
            ->each(fn(array $model) => $this->processModelEntry($model, 'motorcycle', $makeId, $allMakes));

        collect($this->service->getModels($makeId, 'Caravan'))
            ->filter(fn($model) => !empty($model['name']))
            ->each(fn(array $model) => $this->processModelEntry($model, 'caravan', $makeId, $allMakes));

        collect($this->service->getModels($makeId, 'Motorhome'))
            ->filter(fn($model) => !empty($model['name']))
            ->each(fn(array $model) => $this->processModelEntry($model, 'motorhome', $makeId, $allMakes));
    }

    protected function syncBodyStyles()
    {
        collect($this->service->getBodyStyles('Car'))
            ->filter(fn ($body_style) => !empty($body_style['name']))
            ->each(fn (array $body_style) => $this->processBodyStyleEntry($body_style, 'car'));

        collect($this->service->getBodyStyles('Van'))
            ->filter(fn ($body_style) => !empty($body_style['name']))
            ->each(fn (array $body_style) => $this->processBodyStyleEntry($body_style, 'lcv'));

        collect($this->service->getBodyStyles('Bike'))
            ->filter(fn ($body_style) => !empty($body_style['name']))
            ->each(fn (array $body_style) => $this->processBodyStyleEntry($body_style, 'motorcycle'));

        collect($this->service->getBodyStyles('Caravan'))
            ->filter(fn ($body_style) => !empty($body_style['name']))
            ->each(fn (array $body_style) => $this->processBodyStyleEntry($body_style, 'caravan'));

        collect($this->service->getBodyStyles('Motorhome'))
            ->filter(fn ($body_style) => !empty($body_style['name']))
            ->each(fn (array $body_style) => $this->processBodyStyleEntry($body_style, 'motorhome'));
    }

    private function processModelEntry(array $model, string $type, $makeId, $allMakes): void
    {
        // For some reason AT has default A6 as A6 Unspecified
        if ($model['name'] == 'A6 Unspecified') {
            $model['name'] = 'A6';
        }

        // Look up by autotrader_id only (not type) to allow merging types
        $existing = VehicleModel::query()
            ->where('autotrader_id', $model['modelId'])
            ->first();

        // Also check by name + make if not found by autotrader_id
        if (!$existing) {
            $existing = VehicleModel::query()
                ->where('name', $model['name'])
                ->where('make_id', $allMakes[$makeId])
                ->first();
        }

        if (!$existing) {
            VehicleModel::query()->create([
                'name' => $model['name'],
                'make_id' => $allMakes[$makeId],
                'autotrader_id' => $model['modelId'],
                'type' => $type
            ]);
        } elseif (!$existing->do_not_sync) {
            $existing->update([
                'name' => $model['name'],
                'make_id' => $allMakes[$makeId],
                'autotrader_id' => $model['modelId'],
                'type' => $this->mergeTypes($existing->type, $type),
            ]);
        }
    }

    private function mergeTypes(?string $existingType, string $newType): string
    {
        if (empty($existingType)) {
            return $newType;
        }

        $types = explode('-', $existingType);
        if (!in_array($newType, $types, true)) {
            $types[] = $newType;
            sort($types);
        }

        return implode('-', $types);
    }

    private function processBodyStyleEntry(array $body_style, string $type): void
    {
        $existing = BodyStyleType::query()
            ->where('name', $body_style['name'])
            ->where('type', $type)
            ->first();

        if (!$existing) {
            BodyStyleType::query()->create([
                'name' => $body_style['name'],
                'type' => $type,
            ]);
        } elseif (!$existing->do_not_sync) {
            $existing->update([
                'type' => $this->mergeTypes($existing->type, $type),
            ]);
        }
    }

    /**
     * Notify on new additions
     *
     * @return void
     */
    private function notifyOnAdditions()
    {
        $recentMakes = VehicleMake::query()
            ->where('created_at', '>', Carbon::now()->startOfDay())
            ->get();

        $recentModels = VehicleModel::query()
            ->where('created_at', '>', Carbon::now()->startOfDay())
            ->get();

        if ($recentMakes->isEmpty() && $recentModels->isEmpty()) {
            return;
        }

        Mail::to(config('mail.developers'))
            ->send(new NewVehicleMakesModelsMail($recentMakes, $recentModels));
    }
}
