<?php

declare(strict_types=1);

namespace Maestro\Core\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;
use Maestro\Core\Support\PackagePaths;

class MakeCustomFeatureCommand extends Command
{
    protected $signature = 'make:custom-feature
        {name : Feature name in PascalCase or kebab-case}
        {--slug= : maestro_modules.slug (default: custom_{kebab})}
        {--api-prefix= : URL segment under api/custom/ (default: kebab name)}
        {--label= : Human-readable module label}';

    protected $description = 'Scaffold a host custom feature under app/Custom/Features and resources/js/modules/custom';

    public function handle(): int
    {
        $pascal = Str::studly($this->argument('name'));
        $kebab = Str::kebab($pascal);
        $snake = Str::snake($pascal);
        $camel = Str::camel($pascal);
        $slug = $this->option('slug') ?: 'custom_'.$snake;
        $apiPrefix = $this->option('api-prefix') ?: $kebab;
        $label = $this->option('label') ?: $pascal;
        $slugDot = str_replace('_', '.', $slug);

        $replacements = [
            '{{Pascal}}' => $pascal,
            '{{kebab}}' => $kebab,
            '{{snake}}' => $snake,
            '{{camel}}' => $camel,
            '{{slug}}' => $slug,
            '{{slugDot}}' => $slugDot,
            '{{apiPrefix}}' => $apiPrefix,
            '{{label}}' => $label,
        ];

        $written = [];

        $featureBase = base_path('app/Custom/Features/'.$pascal);
        $files = [
            PackagePaths::stubPath('custom-feature/Providers/FeatureServiceProvider.php.stub') => $featureBase.'/Providers/'.$pascal.'ServiceProvider.php',
            PackagePaths::stubPath('custom-feature/database/seeders/FeatureSeeder.php.stub') => $featureBase.'/Database/Seeders/'.$pascal.'Seeder.php',
            PackagePaths::stubPath('custom-feature/routes/custom.php.stub') => base_path('routes/custom/'.$kebab.'.php'),
            PackagePaths::stubPath('custom-feature/resources/js/routes.js.stub') => base_path('resources/js/modules/custom/'.$kebab.'/routes.js'),
            PackagePaths::stubPath('custom-feature/resources/js/api.js.stub') => base_path('resources/js/modules/custom/'.$kebab.'/api.js'),
            PackagePaths::stubPath('custom-feature/resources/js/views/Index.vue.stub') => base_path('resources/js/modules/custom/'.$kebab.'/views/Index.vue'),
        ];

        $migrationName = date('Y_m_d_His').'_create_custom_'.$snake.'_placeholder_table.php';
        $files[PackagePaths::stubPath('custom-feature/database/migrations/create_placeholder_table.php.stub')]
            = $featureBase.'/database/migrations/'.$migrationName;

        foreach ($files as $source => $destination) {
            if (File::exists($destination)) {
                $this->warn('Skipped (exists): '.$this->relativePath($destination));

                continue;
            }

            if (! File::exists($source)) {
                $this->error('Missing stub: '.$source);

                return self::FAILURE;
            }

            File::ensureDirectoryExists(dirname($destination));
            $contents = str_replace(
                array_keys($replacements),
                array_values($replacements),
                File::get($source),
            );
            File::put($destination, $contents);
            $written[] = $destination;
            $this->info('Created: '.$this->relativePath($destination));
        }

        $this->patchCustomServiceProvider($pascal);
        $this->patchCustomRoutes($kebab, $camel);
        $this->patchCustomDatabaseSeeder($pascal);

        if ($written === []) {
            $this->comment('No new files created (all exist). Patches applied where possible.');
        }

        $this->newLine();
        $this->line('Next: register \\App\\Custom\\Features\\'.$pascal.'\\Providers\\'.$pascal.'ServiceProvider::class in CustomServiceProvider.');
        $this->line('Then: php artisan migrate && php artisan db:seed --class=Database\\Seeders\\CustomDatabaseSeeder');

        return self::SUCCESS;
    }

    private function patchCustomServiceProvider(string $pascal): void
    {
        $path = app_path('Providers/CustomServiceProvider.php');
        if (! File::exists($path)) {
            $this->warn('CustomServiceProvider not found — run php artisan core:install first.');

            return;
        }

        $register = '$this->app->register(\\App\\Custom\\Features\\'.$pascal.'\\Providers\\'.$pascal.'ServiceProvider::class);';
        $contents = File::get($path);

        if (str_contains($contents, $register)) {
            return;
        }

        $needle = '// Register feature providers, e.g.:';
        if (str_contains($contents, $needle)) {
            $contents = str_replace(
                $needle,
                $needle."\n        ".$register,
                $contents,
            );
        } else {
            $contents = preg_replace(
                '/(public function register\(\): void\s*\{)/',
                "$1\n        ".$register,
                $contents,
                1,
            ) ?? $contents;
        }

        File::put($path, $contents);
        $this->info('Updated: app/Providers/CustomServiceProvider.php');
    }

    private function patchCustomRoutes(string $kebab, string $camel): void
    {
        $path = base_path('resources/js/modules/custom/routes.js');
        if (! File::exists($path)) {
            $this->warn('resources/js/modules/custom/routes.js not found — run php artisan core:install first.');

            return;
        }

        $contents = File::get($path);
        $import = 'import { '.$camel.'Routes } from "./'.$kebab.'/routes.js";';

        if (! str_contains($contents, $import)) {
            if (preg_match('/^\/\/ import/m', $contents)) {
                $contents = preg_replace(
                    '/^\/\/ import.*$/m',
                    $import,
                    $contents,
                    1,
                ) ?? $contents;
            } else {
                $contents = $import."\n".$contents;
            }
        }

        $spread = '...'.$camel.'Routes,';
        if (! str_contains($contents, $spread)) {
            $contents = preg_replace(
                '/export const customRoutes = \[\s*\n/',
                "export const customRoutes = [\n  ".$spread."\n",
                $contents,
                1,
            ) ?? $contents;
        }

        File::put($path, $contents);
        $this->info('Updated: resources/js/modules/custom/routes.js');
    }

    private function patchCustomDatabaseSeeder(string $pascal): void
    {
        $path = base_path('database/seeders/CustomDatabaseSeeder.php');
        if (! File::exists($path)) {
            $this->warn('CustomDatabaseSeeder not found — run php artisan core:install first.');

            return;
        }

        $class = '\\App\\Custom\\Features\\'.$pascal.'\\Database\\Seeders\\'.$pascal.'Seeder::class,';
        $contents = File::get($path);

        if (str_contains($contents, $class)) {
            return;
        }

        if (preg_match('/\$this->call\(\[\s*\n((?:\s*\/\/.*\n)*)(\s*)\]\);/s', $contents, $matches)) {
            $indent = strlen($matches[2] ?? '') > 0 ? $matches[2] : '            ';
            $contents = preg_replace(
                '/\$this->call\(\[\s*\n(?:\s*\/\/.*\n)*\s*\]\);/s',
                "\$this->call([\n".$indent.$class."\n".$indent.']);',
                $contents,
                1,
            ) ?? $contents;
        } elseif (preg_match('/\$this->call\(\[\s*\n(\s*)\]\);/s', $contents, $matches)) {
            $indent = $matches[1] !== '' ? $matches[1] : '            ';
            $contents = preg_replace(
                '/\$this->call\(\[\s*\n\s*\]\);/s',
                "\$this->call([\n".$indent.$class."\n".$indent.']);',
                $contents,
                1,
            ) ?? $contents;
        }

        File::put($path, $contents);
        $this->info('Updated: database/seeders/CustomDatabaseSeeder.php');
    }

    private function relativePath(string $absolute): string
    {
        $base = base_path().DIRECTORY_SEPARATOR;

        return str_starts_with($absolute, $base)
            ? str_replace('\\', '/', substr($absolute, strlen($base)))
            : $absolute;
    }
}
