<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\File;

class MakeThrillWikiModel extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'make:thrillwiki-model {name : The name of the model}
                            {--migration : Generate a migration file}
                            {--factory : Generate a model factory}
                            {--with-relationships : Include common ThrillWiki relationships}
                            {--cached : Add caching traits and methods}
                            {--api-resource : Generate API resource class}
                            {--with-tests : Generate model tests}
                            {--force : Overwrite existing files}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Generate a ThrillWiki model with optimized patterns, traits, and optional related files';

    /**
     * ThrillWiki traits for different model types
     */
    protected array $thrillWikiTraits = [
        'HasLocation' => 'App\\Traits\\HasLocation',
        'HasSlugHistory' => 'App\\Traits\\HasSlugHistory', 
        'HasStatistics' => 'App\\Traits\\HasStatistics',
        'HasCaching' => 'App\\Traits\\HasCaching',
        'HasSoftDeletes' => 'Illuminate\\Database\\Eloquent\\SoftDeletes',
        'HasFactory' => 'Illuminate\\Database\\Eloquent\\Factories\\HasFactory',
    ];

    /**
     * Common ThrillWiki relationships by model type
     */
    protected array $relationshipPatterns = [
        'Park' => [
            'areas' => 'hasMany:ParkArea',
            'rides' => 'hasManyThrough:Ride,ParkArea',
            'operator' => 'belongsTo:Operator',
            'photos' => 'morphMany:Photo',
            'reviews' => 'morphMany:Review',
        ],
        'Ride' => [
            'park' => 'belongsTo:Park',
            'area' => 'belongsTo:ParkArea',
            'manufacturer' => 'belongsTo:Manufacturer',
            'designer' => 'belongsTo:Designer',
            'photos' => 'morphMany:Photo',
            'reviews' => 'morphMany:Review',
        ],
        'Operator' => [
            'parks' => 'hasMany:Park',
        ],
        'Manufacturer' => [
            'rides' => 'hasMany:Ride,manufacturer_id',
        ],
        'Designer' => [
            'rides' => 'hasMany:Ride,designer_id',
        ],
        'Review' => [
            'user' => 'belongsTo:User',
            'reviewable' => 'morphTo',
        ],
    ];

    /**
     * Execute the console command.
     */
    public function handle()
    {
        $this->info('🚀 Generating ThrillWiki Model for: ' . $this->argument('name'));

        $name = $this->argument('name');
        $className = Str::studly($name);
        $tableName = Str::snake(Str::plural($name));

        // Generate model
        $this->generateModel($className);

        // Generate optional files
        if ($this->option('migration')) {
            $this->generateMigration($className, $tableName);
        }

        if ($this->option('factory')) {
            $this->generateFactory($className);
        }

        if ($this->option('api-resource')) {
            $this->generateApiResource($className);
        }

        if ($this->option('with-tests')) {
            $this->generateTests($className);
        }

        $this->displaySummary($className);

        return Command::SUCCESS;
    }

    /**
     * Generate the model file
     */
    protected function generateModel(string $className): void
    {
        $modelPath = app_path("Models/{$className}.php");

        if (File::exists($modelPath) && !$this->option('force')) {
            $this->error("Model {$className} already exists! Use --force to overwrite.");
            return;
        }

        $modelContent = $this->buildModelContent($className);

        $this->ensureDirectoryExists(dirname($modelPath));
        File::put($modelPath, $modelContent);

        $this->line("✅ Model created: app/Models/{$className}.php");
    }

    /**
     * Build the model content with ThrillWiki patterns
     */
    protected function buildModelContent(string $className): string
    {
        $tableName = Str::snake(Str::plural($className));
        $traits = $this->getTraitsForModel($className);
        $relationships = $this->getRelationshipsForModel($className);
        $cachingMethods = $this->option('cached') ? $this->getCachingMethods($className) : '';

        $stub = <<<'PHP'
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
{TRAIT_IMPORTS}

/**
 * {CLASS_NAME} Model
 * 
 * Generated by ThrillWiki Model Generator
 * Includes ThrillWiki optimization patterns and performance enhancements
 */
class {CLASS_NAME} extends Model
{
{TRAITS}

    /**
     * The table associated with the model.
     *
     * @var string
     */
    protected $table = '{TABLE_NAME}';

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'description',
        'is_active',
        // Add more fillable attributes as needed
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'is_active' => 'boolean',
        'created_at' => 'datetime',
        'updated_at' => 'datetime',
        // Add more casts as needed
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        // Add hidden attributes if needed
    ];

    // Query Scopes
    
    /**
     * Scope a query to only include active records.
     */
    public function scopeActive($query)
    {
        return $query->where('is_active', true);
    }

    /**
     * Scope for optimized queries with common relationships.
     */
    public function scopeOptimized($query)
    {
        return $query->with($this->getOptimizedRelations());
    }

    // ThrillWiki Methods

    /**
     * Get optimized relations for this model.
     */
    public function getOptimizedRelations(): array
    {
        return [
            // Define common relationships to eager load
        ];
    }

    /**
     * Get cache key for this model instance.
     */
    public function getCacheKey(string $suffix = ''): string
    {
        $key = strtolower(class_basename($this)) . '.' . $this->id;
        return $suffix ? $key . '.' . $suffix : $key;
    }

{RELATIONSHIPS}

{CACHING_METHODS}
}
PHP;

        return str_replace([
            '{CLASS_NAME}',
            '{TABLE_NAME}',
            '{TRAIT_IMPORTS}',
            '{TRAITS}',
            '{RELATIONSHIPS}',
            '{CACHING_METHODS}',
        ], [
            $className,
            $tableName,
            $this->buildTraitImports($traits),
            $this->buildTraitUses($traits),
            $relationships,
            $cachingMethods,
        ], $stub);
    }

    /**
     * Get traits for the model based on options and model type
     */
    protected function getTraitsForModel(string $className): array
    {
        $traits = ['HasFactory']; // Always include HasFactory

        // Add SoftDeletes for most models
        $traits[] = 'HasSoftDeletes';

        // Add caching if requested
        if ($this->option('cached')) {
            $traits[] = 'HasCaching';
        }

        // Add location trait for location-based models
        if (in_array($className, ['Park', 'Company', 'ParkArea'])) {
            $traits[] = 'HasLocation';
        }

        // Add slug history for main entities
        if (in_array($className, ['Park', 'Ride', 'Company', 'Designer'])) {
            $traits[] = 'HasSlugHistory';
        }

        // Add statistics for countable entities
        if (in_array($className, ['Park', 'Ride', 'User'])) {
            $traits[] = 'HasStatistics';
        }

        return $traits;
    }

    /**
     * Build trait import statements
     */
    protected function buildTraitImports(array $traits): string
    {
        $imports = [];
        foreach ($traits as $trait) {
            if (isset($this->thrillWikiTraits[$trait])) {
                $imports[] = "use {$this->thrillWikiTraits[$trait]};";
            }
        }
        return implode("\n", $imports);
    }

    /**
     * Build trait use statements
     */
    protected function buildTraitUses(array $traits): string
    {
        $uses = array_map(function($trait) {
            return "    use {$trait};";
        }, $traits);
        
        return implode("\n", $uses);
    }

    /**
     * Get relationships for the model
     */
    protected function getRelationshipsForModel(string $className): string
    {
        if (!$this->option('with-relationships')) {
            return '';
        }

        if (!isset($this->relationshipPatterns[$className])) {
            return '';
        }

        $relationships = [];
        foreach ($this->relationshipPatterns[$className] as $method => $definition) {
            $relationships[] = $this->buildRelationshipMethod($method, $definition);
        }

        return "\n    // Relationships\n\n" . implode("\n\n", $relationships);
    }

    /**
     * Build a relationship method
     */
    protected function buildRelationshipMethod(string $method, string $definition): string
    {
        [$type, $model, $foreignKey] = array_pad(explode(',', str_replace(':', ',', $definition)), 3, null);
        
        $methodBody = match($type) {
            'hasMany' => $foreignKey ? 
                "return \$this->hasMany({$model}::class, '{$foreignKey}');" :
                "return \$this->hasMany({$model}::class);",
            'belongsTo' => $foreignKey ? 
                "return \$this->belongsTo({$model}::class, '{$foreignKey}');" :
                "return \$this->belongsTo({$model}::class);",
            'hasManyThrough' => "return \$this->hasManyThrough({$model}::class, {$foreignKey}::class);",
            'morphMany' => "return \$this->morphMany({$model}::class, 'morphable');",
            'morphTo' => "return \$this->morphTo();",
            default => "return \$this->{$type}({$model}::class);"
        };

        return "    /**\n     * Get the {$method} relationship.\n     */\n    public function {$method}()\n    {\n        {$methodBody}\n    }";
    }

    /**
     * Get caching methods for the model
     */
    protected function getCachingMethods(string $className): string
    {
        return <<<'PHP'

    // Caching Methods

    /**
     * Remember a value in cache with model-specific key.
     */
    public function remember(string $key, $callback, int $ttl = 3600)
    {
        return cache()->remember($this->getCacheKey($key), $ttl, $callback);
    }

    /**
     * Invalidate cache for this model.
     */
    public function invalidateCache(string $key = null): void
    {
        if ($key) {
            cache()->forget($this->getCacheKey($key));
        } else {
            // Clear all cache keys for this model
            cache()->forget($this->getCacheKey());
        }
    }

    /**
     * Boot method to handle cache invalidation.
     */
    protected static function boot()
    {
        parent::boot();

        static::saved(function ($model) {
            $model->invalidateCache();
        });

        static::deleted(function ($model) {
            $model->invalidateCache();
        });
    }
PHP;
    }

    /**
     * Generate migration file
     */
    protected function generateMigration(string $className, string $tableName): void
    {
        $migrationName = 'create_' . $tableName . '_table';
        $timestamp = date('Y_m_d_His');
        $migrationFile = database_path("migrations/{$timestamp}_{$migrationName}.php");

        $migrationContent = $this->buildMigrationContent($className, $tableName, $migrationName);

        File::put($migrationFile, $migrationContent);
        $this->line("✅ Migration created: database/migrations/{$timestamp}_{$migrationName}.php");
    }

    /**
     * Build migration content
     */
    protected function buildMigrationContent(string $className, string $tableName, string $migrationName): string
    {
        $migrationClass = Str::studly($migrationName);

        $stub = <<<'PHP'
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('{TABLE_NAME}', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->text('description')->nullable();
            $table->boolean('is_active')->default(true);
            
            // Add common ThrillWiki fields
            $table->string('slug')->unique();
            
            // Add indexes for performance
            $table->index(['is_active']);
            $table->index(['name']);
            $table->index(['slug']);
            
            $table->timestamps();
            $table->softDeletes();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('{TABLE_NAME}');
    }
};
PHP;

        return str_replace('{TABLE_NAME}', $tableName, $stub);
    }

    /**
     * Generate factory file
     */
    protected function generateFactory(string $className): void
    {
        $factoryPath = database_path("factories/{$className}Factory.php");

        if (File::exists($factoryPath) && !$this->option('force')) {
            $this->error("Factory {$className}Factory already exists! Use --force to overwrite.");
            return;
        }

        $factoryContent = $this->buildFactoryContent($className);

        $this->ensureDirectoryExists(dirname($factoryPath));
        File::put($factoryPath, $factoryContent);

        $this->line("✅ Factory created: database/factories/{$className}Factory.php");
    }

    /**
     * Build factory content
     */
    protected function buildFactoryContent(string $className): string
    {
        $stub = <<<'PHP'
<?php

namespace Database\Factories;

use App\Models\{CLASS_NAME};
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;

/**
 * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\{CLASS_NAME}>
 */
class {CLASS_NAME}Factory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var string
     */
    protected $model = {CLASS_NAME}::class;

    /**
     * Define the model's default state.
     *
     * @return array<string, mixed>
     */
    public function definition(): array
    {
        $name = $this->faker->unique()->words(2, true);
        
        return [
            'name' => $name,
            'slug' => Str::slug($name),
            'description' => $this->faker->paragraphs(2, true),
            'is_active' => $this->faker->boolean(90), // 90% chance of being active
            'created_at' => $this->faker->dateTimeBetween('-1 year', 'now'),
            'updated_at' => function (array $attributes) {
                return $this->faker->dateTimeBetween($attributes['created_at'], 'now');
            },
        ];
    }

    /**
     * Indicate that the model is active.
     */
    public function active(): static
    {
        return $this->state(fn (array $attributes) => [
            'is_active' => true,
        ]);
    }

    /**
     * Indicate that the model is inactive.
     */
    public function inactive(): static
    {
        return $this->state(fn (array $attributes) => [
            'is_active' => false,
        ]);
    }
}
PHP;

        return str_replace('{CLASS_NAME}', $className, $stub);
    }

    /**
     * Generate API resource
     */
    protected function generateApiResource(string $className): void
    {
        $resourcePath = app_path("Http/Resources/{$className}Resource.php");

        if (File::exists($resourcePath) && !$this->option('force')) {
            $this->error("Resource {$className}Resource already exists! Use --force to overwrite.");
            return;
        }

        $resourceContent = $this->buildApiResourceContent($className);

        $this->ensureDirectoryExists(dirname($resourcePath));
        File::put($resourcePath, $resourceContent);

        $this->line("✅ API Resource created: app/Http/Resources/{$className}Resource.php");
    }

    /**
     * Build API resource content
     */
    protected function buildApiResourceContent(string $className): string
    {
        $stub = <<<'PHP'
<?php

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

/**
 * {CLASS_NAME} API Resource
 * 
 * Transforms {CLASS_NAME} model data for API responses
 * Includes ThrillWiki optimization patterns
 */
class {CLASS_NAME}Resource extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @return array<string, mixed>
     */
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'slug' => $this->slug,
            'description' => $this->description,
            'is_active' => $this->is_active,
            'created_at' => $this->created_at?->toISOString(),
            'updated_at' => $this->updated_at?->toISOString(),
            
            // Include relationships when loaded
            $this->mergeWhen($this->relationLoaded('relationships'), [
                // Add relationship data here
            ]),
        ];
    }

    /**
     * Get additional data that should be returned with the resource array.
     *
     * @return array<string, mixed>
     */
    public function with(Request $request): array
    {
        return [
            'meta' => [
                'model' => '{CLASS_NAME}',
                'generated_at' => now()->toISOString(),
            ],
        ];
    }
}
PHP;

        return str_replace('{CLASS_NAME}', $className, $stub);
    }

    /**
     * Generate test files
     */
    protected function generateTests(string $className): void
    {
        $testPath = base_path("tests/Feature/{$className}Test.php");

        if (File::exists($testPath) && !$this->option('force')) {
            $this->error("Test {$className}Test already exists! Use --force to overwrite.");
            return;
        }

        $testContent = $this->buildTestContent($className);

        $this->ensureDirectoryExists(dirname($testPath));
        File::put($testPath, $testContent);

        $this->line("✅ Test created: tests/Feature/{$className}Test.php");
    }

    /**
     * Build test content
     */
    protected function buildTestContent(string $className): string
    {
        $stub = <<<'PHP'
<?php

namespace Tests\Feature;

use App\Models\{CLASS_NAME};
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;

/**
 * {CLASS_NAME} Model Feature Tests
 * 
 * Tests for ThrillWiki {CLASS_NAME} model functionality
 */
class {CLASS_NAME}Test extends TestCase
{
    use RefreshDatabase, WithFaker;

    /**
     * Test model creation.
     */
    public function test_can_create_{LOWER_CLASS_NAME}(): void
    {
        ${LOWER_CLASS_NAME} = {CLASS_NAME}::factory()->create();

        $this->assertDatabaseHas('{TABLE_NAME}', [
            'id' => ${LOWER_CLASS_NAME}->id,
            'name' => ${LOWER_CLASS_NAME}->name,
        ]);
    }

    /**
     * Test model factory.
     */
    public function test_{LOWER_CLASS_NAME}_factory_works(): void
    {
        ${LOWER_CLASS_NAME} = {CLASS_NAME}::factory()->create();

        $this->assertInstanceOf({CLASS_NAME}::class, ${LOWER_CLASS_NAME});
        $this->assertNotEmpty(${LOWER_CLASS_NAME}->name);
        $this->assertIsBool(${LOWER_CLASS_NAME}->is_active);
    }

    /**
     * Test active scope.
     */
    public function test_active_scope_filters_correctly(): void
    {
        {CLASS_NAME}::factory()->active()->create();
        {CLASS_NAME}::factory()->inactive()->create();

        $activeCount = {CLASS_NAME}::active()->count();
        $totalCount = {CLASS_NAME}::count();

        $this->assertEquals(1, $activeCount);
        $this->assertEquals(2, $totalCount);
    }

    /**
     * Test cache key generation.
     */
    public function test_cache_key_generation(): void
    {
        ${LOWER_CLASS_NAME} = {CLASS_NAME}::factory()->create();

        $cacheKey = ${LOWER_CLASS_NAME}->getCacheKey();
        $expectedKey = strtolower('{LOWER_CLASS_NAME}') . '.' . ${LOWER_CLASS_NAME}->id;

        $this->assertEquals($expectedKey, $cacheKey);
    }

    /**
     * Test cache key with suffix.
     */
    public function test_cache_key_with_suffix(): void
    {
        ${LOWER_CLASS_NAME} = {CLASS_NAME}::factory()->create();

        $cacheKey = ${LOWER_CLASS_NAME}->getCacheKey('details');
        $expectedKey = strtolower('{LOWER_CLASS_NAME}') . '.' . ${LOWER_CLASS_NAME}->id . '.details';

        $this->assertEquals($expectedKey, $cacheKey);
    }

    /**
     * Test soft deletes.
     */
    public function test_soft_deletes_work(): void
    {
        ${LOWER_CLASS_NAME} = {CLASS_NAME}::factory()->create();
        ${LOWER_CLASS_NAME}->delete();

        $this->assertSoftDeleted(${LOWER_CLASS_NAME});
        
        // Test that it's excluded from normal queries
        $this->assertEquals(0, {CLASS_NAME}::count());
        
        // Test that it's included in withTrashed queries
        $this->assertEquals(1, {CLASS_NAME}::withTrashed()->count());
    }
}
PHP;

        return str_replace([
            '{CLASS_NAME}',
            '{LOWER_CLASS_NAME}',
            '{TABLE_NAME}',
        ], [
            $className,
            strtolower($className),
            Str::snake(Str::plural($className)),
        ], $stub);
    }

    /**
     * Ensure directory exists
     */
    protected function ensureDirectoryExists(string $directory): void
    {
        if (!File::isDirectory($directory)) {
            File::makeDirectory($directory, 0755, true);
        }
    }

    /**
     * Display summary of generated files
     */
    protected function displaySummary(string $className): void
    {
        $this->newLine();
        $this->info("🎉 ThrillWiki Model '{$className}' created successfully!");
        $this->newLine();

        $this->line("📁 Files Generated:");
        $this->line("   • app/Models/{$className}.php");

        if ($this->option('migration')) {
            $this->line("   • database/migrations/[timestamp]_create_" . Str::snake(Str::plural($className)) . "_table.php");
        }

        if ($this->option('factory')) {
            $this->line("   • database/factories/{$className}Factory.php");
        }

        if ($this->option('api-resource')) {
            $this->line("   • app/Http/Resources/{$className}Resource.php");
        }

        if ($this->option('with-tests')) {
            $this->line("   • tests/Feature/{$className}Test.php");
        }

        $this->newLine();
        $this->line("🚀 Next Steps:");
        
        if ($this->option('migration')) {
            $this->line("   1. Run migration: php artisan migrate");
        }
        
        if ($this->option('with-tests')) {
            $this->line("   2. Run tests: php artisan test --filter {$className}Test");
        }
        
        $this->line("   3. Customize model attributes and relationships");
        $this->line("   4. Update migration with specific fields");
        
        if ($this->option('factory')) {
            $this->line("   5. Customize factory with realistic data");
        }

        $this->newLine();
    }
}