<?php

namespace Wizzard\MagicForger\Generator;

use DB;

use Illuminate\Console\GeneratorCommand;
use Illuminate\Support\Str;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

use Wizzard\MagicForger\Generator\Replacer;

abstract class BaseGenerator extends GeneratorCommand
{
    use Replacer;

    /**
     * The console command description.
     *
     * @var string
     */
    protected $schema;

    /**
     * The console command description.
     *
     * @var string
     */
    protected $tables;

    /**
     * Execute the console command.
     */
    public function handle()
    {


        // First we need to ensure that the table exists, then we can
        if (! $this->tableExists($this->getTableInput())) {
            $this->components->error('The table: "'.$this->getTableInput().'" does not exist in the database.');

            return false;
        }

        $path = $this->getPath();

        $file = $this->getFile($path);

        $file = $this->apply_replacements($file);

        $this->makeDirectory($path);

        $this->files->put($path, $this->sortImports($file));

        $info = $this->type;

        $this->components->info(sprintf('%s [%s] created successfully.', $info, $path));
    }

    /**
     * Override the original so that we can prompt for a table with autocomplete.
     *
     */
    protected function promptForMissingArguments(InputInterface $input, OutputInterface $output)
    {
        $prompted = false;
        if(is_null($input->getArgument('table'))) {
            $prompted = true;
            $table = null;
            while ($table === null) {
                $table = $this->components->askWithCompletion(
                    'What Table should we use?',
                    $this->possibleTables()
                );
            }

            $input->setArgument('table', $table);
        }

        parent::promptForMissingArguments($input, $output);

        // This will get missed if we prompt here but not in the parent
        if($prompted) {
            $this->afterPromptingForMissingArguments($input, $output);
        }
    }

    /**
     * Get the console command arguments.
     *
     * @return array
     */
    protected function getArguments()
    {
        return [
            ['table', InputOption::VALUE_REQUIRED, 'The table to generate files for.'],
        ];
    }

    /**
     * Prompt for missing input arguments using the returned questions.
     *
     * @return array
     */
    protected function promptForMissingArgumentsUsing()
    {
        return [
        ];
    }

    /**
     * Get the console command options.
     *
     * @return array
     */
    protected function getOptions()
    {
        return [
            ['fresh', 'f', InputOption::VALUE_NONE, 'Start from the stub or use existing if possible.'],
        ];
    }

    /**
     * Interact further with the user if they were prompted for missing arguments.
     *
     * @return void
     */
    protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output)
    {
    }

    /**
     * Determines if the file exists
     */
    protected function fileExists(string $path): bool
    {
        return $this->files->exists($path);
    }

    /**
     * Gets the file that will be worked on. If there is already an existing file
     * then we can open that. However if we are forcing the operation, then we
     * will start with an empty stub.
     *
     */
    protected function getFile($name)
    {
        if ((! $this->hasOption('fresh') ||
             ! $this->option('fresh')) &&
             $this->fileExists($name)) {
            //Working with an existing file
            return $this->files->get($name);
        }

        //Working with a stub
        return $this->files->get($this->getStub());
    }

    /**
     * Get the desired class table from the input.
     *
     * @return string
     */
    protected function getTableInput()
    {
        return trim($this->argument('table'));
    }


    /**
     * Determines if the table exists in the current database
     */
    protected function tableExists(string $table_name): bool
    {
        return in_array($table_name, $this->getTables());
    }

    /**
     * Get a list of possible table names.
     */
    protected function possibleTables()
    {
        return $this->getTables();
    }

    /**
     * Get the tables in the schema
     */
    protected function getTables()
    {
        if (is_null($this->tables)) {
            $this->tables = collect($this->getSchema()->listTableNames())->all();
        }

        return $this->tables;
    }

    /**
     * Get the database schema for DB interactions
     */
    protected function getSchema()
    {
        if (is_null($this->schema)) {
            $this->schema = DB::connection()->getDoctrineSchemaManager();
        }

        return $this->schema;
    }
}
