<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;

class CreateModuleCommand extends Command
{
    protected $files;
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'make:module {name}
                                        {--all : Create ready module}
    ';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Module creation command';

    /**
     * Create a new command instance
     * 
     * @return void
     */
    public function __construct(Filesystem $files)
    {
        parent::__construct();

        $this->files = $files;
    }

    /**
     * Execute the console command.
     */
    public function handle()
    {
        if ($this->option('all'))
        {
            $directory = $this->getDirectory($this->argument('name'));
            if ($this->files->isDirectory($directory))
            {
                $this->error('Module already exists!');
            }
            else
            {
                $this->createProviders();
                $this->createConfig();

                $this->createModel();
                $this->createMigration();

                $this->createController();
                $this->createView();
            }
        }
    }

    private function getDirectory($directory)
    {
        return $this->laravel['path'] .
            '/Modules/' . str_replace('\\', '/', $directory);
    }

    private function createModel()
    {
        try
        {
            $model = Str::singular(class_basename($this->argument('name')));
            $this->call('make:model', [
                'name' => 'App\\Modules\\' . trim($this->argument('name')) . '\\Models\\' . $model
            ]);
        }
        catch (\Exception $e)
        {
            $e->getMessage();
        }

    }

    private function createView()
    {
        $directoryName = Str::singular(Str::studly(class_basename($this->argument('name'))));
        $path = $this->getViewsPath($this->argument('name'));
        if ($this->alreadyExists($path))
        {
            $this->error('View already exists!');
        }
        else
        {
            $this->makeDirectory($path);

            $stub = $this->files->get(base_path('resources/stubs/view.stub'));
            $this->files->put($path, $stub);
            $this->info('Views created');

            //livewire
            $path = $this->getLivewireViewsPath($this->argument('name'));
            $this->makeDirectory($path);
            $this->info('Views - livewire folder created');

            //components
            $path = $this->getComponentsViewsPath($this->argument('name'));
            $this->makeDirectory($path);
            $this->info('Views - components folder created');
        }
    }

    private function getViewsPath($argument)
    {
        return $this->laravel['path'] .
            '/Modules/' . str_replace('\\', '/', $argument) . '/Views/index.blade.php';
    }

    private function getLivewireViewsPath($argument)
    {
        return $this->laravel['path'] .
            '/Modules/' . str_replace('\\', '/', $argument) . '/Views/livewire/example.php';
    }

    private function getComponentsViewsPath($argument)
    {
        return $this->laravel['path'] .
            '/Modules/' . str_replace('\\', '/', $argument) . '/Views/components/example.php';
    }

    private function createConfig()
    {
        $directoryName = Str::singular(Str::studly(class_basename($this->argument('name'))));
        $path = $this->getConfigPath($this->argument('name'));
        if ($this->alreadyExists($path))
        {
            $this->error('Config already exists!');
        }
        else
        {
            $this->makeDirectory($path);

            $stub = $this->files->get(base_path('resources/stubs/config.stub'));
            $this->files->put($path, $stub);
            $this->info('Config created');
        }
    }

    private function getConfigPath($argument)
    {
        return $this->laravel['path'] .
            '/Modules/' . str_replace('\\', '/', $argument) . '/Config/config.php';
    }

    private function createProviders()
    {
        //Create ModuleServiceProvider
        $directoryName = Str::singular(Str::studly(class_basename($this->argument('name'))));
        $path = $this->getModuleServiceProviderPath($this->argument('name'));
        if ($this->alreadyExists($path))
        {
            $this->error('ModuleServiceProvider already exists!');
        }
        else
        {
            $this->makeDirectory($path);
            $stub = $this->files->get(base_path('resources/stubs/module_service_provider.stub'));
            $stub = str_replace(
                [
                    'TeamplateDirectoryName',
                ],
                [
                    $directoryName
                ],
                $stub
            );

            $this->files->put($path, $stub);
            $this->info('ModuleServiceProvider created');
        }

        //Create RouteServiceProvider
        $path = $this->getRouteServiceProviderPath($this->argument('name'));
        if ($this->alreadyExists($path))
        {
            $this->error('RouteServiceProvider already exists!');
        }
        else
        {
            $this->makeDirectory($path);
            $stub = $this->files->get(base_path('resources/stubs/route_service_provider.stub'));
            $stub = str_replace(
                [
                    'TeamplateDirectoryName',
                ],
                [
                    $directoryName
                ],
                $stub
            );

            $this->files->put($path, $stub);
            $this->info('RouteServiceProvider created');
        }
    }

    private function getModuleServiceProviderPath($argument)
    {
        return $this->laravel['path'] .
            '/Modules/' . str_replace('\\', '/', $argument) . '/Providers/ModuleServiceProvider.php';
    }

    private function getRouteServiceProviderPath($argument)
    {
        return $this->laravel['path'] .
            '/Modules/' . str_replace('\\', '/', $argument) . '/Providers/RouteServiceProvider.php';
    }

    private function createController()
    {
        $controller = Str::studly(class_basename($this->argument('name')));
        $modelName = Str::singular(Str::studly(class_basename($this->argument('name'))));

        $path = $this->getControllerPath($this->argument('name'));

        if ($this->alreadyExists($path))
        {
            $this->error('Controller already exists!');
        }
        else
        {
            $this->makeDirectory($path);

            $stub = $this->files->get(base_path('resources/stubs/controller.model.stub'));

            $stub = str_replace(
                [
                    'TeamplateNamespace',
                    'TeamplateFullModelClass',
                    'TeamplateClass',
                    'TeamplateDirectoryName'
                ],
                [
                    'Modules\\' . trim($this->argument('name')) . '\\Http\\Controllers',
                    'Modules\\' . trim($this->argument('name')) . '\\Models\\' . $modelName,
                    $controller . 'Controller',
                    lcfirst($controller)

                ],
                $stub
            );

            $this->files->put($path, $stub);
            $this->info('Controller created');

            //Livewire Http
            $path = $this->getLivewireHttpPath($this->argument('name'));
            $this->makeDirectory($path);
            $this->info('Http - Livewire folder created');

            //Components Http
            $path = $this->getComponentsHttpPath($this->argument('name'));
            $this->makeDirectory($path);
            $this->info('Http - Components folder created');

            //Policies Http
            $path = $this->getPoliciesHttpPath($this->argument('name'));
            $this->makeDirectory($path);
            $this->info('Http - Policies folder created');

            //Requests Http
            $path = $this->getRequestsHttpPath($this->argument('name'));
            $this->makeDirectory($path);
            $this->info('Http - Requests folder created');

            //Routes create
            $this->createRoutes($controller, $modelName);
        }
    }

    private function getLivewireHttpPath($argument)
    {
        return $this->laravel['path'] .
            '/Modules/' . str_replace('\\', '/', $argument) . '/Http/Livewire/example.php';
    }

    private function getComponentsHttpPath($argument)
    {
        return $this->laravel['path'] .
            '/Modules/' . str_replace('\\', '/', $argument) . '/Http/Components/example.php';
    }

    private function getPoliciesHttpPath($argument)
    {
        return $this->laravel['path'] .
            '/Modules/' . str_replace('\\', '/', $argument) . '/Http/Policies/example.php';
    }

    private function getRequestsHttpPath($argument)
    {
        return $this->laravel['path'] .
            '/Modules/' . str_replace('\\', '/', $argument) . '/Http/Requests/example.php';
    }

    private function createRoutes($controller, $modelName)
    {
        $routePath = $this->getRoutesPath($this->argument('name'));

        if ($this->alreadyExists($routePath))
        {
            $this->error('Routes already exists!');
        }
        else
        {
            $this->makeDirectory($routePath);

            $stub = $this->files->get(base_path('resources/stubs/routes.web.stub'));

            $stub = str_replace(
                [
                    'TeamplateDirectoryName',
                    'TeamplateRoutePrefix',
                    'TeamplateClass'
                ],
                [
                    $controller,
                    lcfirst($modelName),
                    $controller . 'Controller'
                ],
                $stub
            );

            $this->files->put($routePath, $stub);
            $this->info('Routes created successfully');
        }
    }

    private function alreadyExists($valuePath)
    {
        return $this->files->exists($valuePath);
    }

    private function getRoutesPath($argument)
    {
        return $this->laravel['path'] .
            '/Modules/' .
            str_replace('\\', '/', $argument) .
            '/Routes/web.php';
    }

    private function getControllerPath($argument)
    {
        $controller = Str::studly(class_basename($argument));
        return $this->laravel['path'] .
            '/Modules/' .
            str_replace('\\', '/', $argument) .
            '/Http/Controllers/' .
            $controller .
            'Controller.php';
    }

    private function makeDirectory($path)
    {
        $this->files->makeDirectory(dirname($path), 0777, true, true);
        //$this->files->makeDirectory(dirname($path));
    }

    private function createMigration()
    {
        $name = trim($this->argument('name'));
        $table = Str::plural(Str::snake(class_basename($this->argument('name'))));
        try
        {
            $this->call('make:migration', [
                'name'     => 'create_' . $table . '_table',
                '--create' => $table,
                '--path'   => 'app/Modules/' . $name . '/Database/Migrations'
            ]);
        }
        catch (\Exception $e)
        {
            $e->getMessage();
        }
    }
}
