<?php

namespace IAWPSCOPED\Illuminate\Database\Console;

use IAWPSCOPED\Illuminate\Console\Command;
use IAWPSCOPED\Illuminate\Contracts\Events\Dispatcher;
use IAWPSCOPED\Illuminate\Database\Eloquent\MassPrunable;
use IAWPSCOPED\Illuminate\Database\Eloquent\Prunable;
use IAWPSCOPED\Illuminate\Database\Eloquent\SoftDeletes;
use IAWPSCOPED\Illuminate\Database\Events\ModelsPruned;
use IAWPSCOPED\Illuminate\Support\Str;
use InvalidArgumentException;
use IAWPSCOPED\Symfony\Component\Finder\Finder;
/** @internal */
class PruneCommand extends Command
{
    /**
     * The console command name.
     *
     * @var string
     */
    protected $signature = 'model:prune
                                {--model=* : Class names of the models to be pruned}
                                {--except=* : Class names of the models to be excluded from pruning}
                                {--chunk=1000 : The number of models to retrieve per chunk of models to be deleted}
                                {--pretend : Display the number of prunable records found instead of deleting them}';
    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Prune models that are no longer needed';
    /**
     * Execute the console command.
     *
     * @param  \Illuminate\Contracts\Events\Dispatcher  $events
     * @return void
     */
    public function handle(Dispatcher $events)
    {
        $models = $this->models();
        if ($models->isEmpty()) {
            $this->info('No prunable models found.');
            return;
        }
        if ($this->option('pretend')) {
            $models->each(function ($model) {
                $this->pretendToPrune($model);
            });
            return;
        }
        $events->listen(ModelsPruned::class, function ($event) {
            $this->info("{$event->count} [{$event->model}] records have been pruned.");
        });
        $models->each(function ($model) {
            $instance = new $model();
            $chunkSize = \property_exists($instance, 'prunableChunkSize') ? $instance->prunableChunkSize : $this->option('chunk');
            $total = $this->isPrunable($model) ? $instance->pruneAll($chunkSize) : 0;
            if ($total == 0) {
                $this->info("No prunable [{$model}] records found.");
            }
        });
        $events->forget(ModelsPruned::class);
    }
    /**
     * Determine the models that should be pruned.
     *
     * @return \Illuminate\Support\Collection
     */
    protected function models()
    {
        if (!empty($models = $this->option('model'))) {
            return \IAWPSCOPED\collect($models)->filter(function ($model) {
                return \class_exists($model);
            })->values();
        }
        $except = $this->option('except');
        if (!empty($models) && !empty($except)) {
            throw new InvalidArgumentException('The --models and --except options cannot be combined.');
        }
        return \IAWPSCOPED\collect((new Finder())->in($this->getDefaultPath())->files()->name('*.php'))->map(function ($model) {
            $namespace = $this->laravel->getNamespace();
            return $namespace . \str_replace(['/', '.php'], ['\\', ''], Str::after($model->getRealPath(), \realpath(app_path()) . \DIRECTORY_SEPARATOR));
        })->when(!empty($except), function ($models) use($except) {
            return $models->reject(function ($model) use($except) {
                return \in_array($model, $except);
            });
        })->filter(function ($model) {
            return $this->isPrunable($model);
        })->filter(function ($model) {
            return \class_exists($model);
        })->values();
    }
    /**
     * Get the default path where models are located.
     *
     * @return string
     */
    protected function getDefaultPath()
    {
        return app_path('Models');
    }
    /**
     * Determine if the given model class is prunable.
     *
     * @param  string  $model
     * @return bool
     */
    protected function isPrunable($model)
    {
        $uses = class_uses_recursive($model);
        return \in_array(Prunable::class, $uses) || \in_array(MassPrunable::class, $uses);
    }
    /**
     * Display how many models will be pruned.
     *
     * @param  string  $model
     * @return void
     */
    protected function pretendToPrune($model)
    {
        $instance = new $model();
        $count = $instance->prunable()->when(\in_array(SoftDeletes::class, class_uses_recursive(\get_class($instance))), function ($query) {
            $query->withTrashed();
        })->count();
        if ($count === 0) {
            $this->info("No prunable [{$model}] records found.");
        } else {
            $this->info("{$count} [{$model}] records will be pruned.");
        }
    }
}
