<?php

declare(strict_types=1);

namespace App\Filament\Concerns;

use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\DateTimePicker;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Schemas\Components\Component;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;
use ReflectionClass;

trait CanGenerateResourceForms
{
    /**
     * Get available custom form components from App\Filament\Components\Form.
     */
    protected function getCustomFormComponents(): array
    {
        $components = [];
        $formComponentsPath = app_path('Filament/Components/Form');

        if (! File::exists($formComponentsPath)) {
            return [];
        }

        foreach (File::files($formComponentsPath) as $file) {
            $className = 'App\\Filament\\Components\\Form\\' . Str::before($file->getFilename(), '.php');
            if (! class_exists($className)) {
                continue;
            }

            $reflection = new ReflectionClass($className);
            if ($reflection->isAbstract() || ! $reflection->isSubclassOf(Component::class)) {
                continue;
            }

            // Derive a column name from the component class
            $componentName = $reflection->getShortName();
            $types = [
                'Select', 'TextInput', 'Repeater', 'FileUpload',
                'DateTimePicker', 'KeyValue', 'Hidden', 'DatePicker',
                'Textarea', 'TimePicker', 'ColorPicker', 'Toggle', 'TagsInput',
            ];
            foreach ($types as $type) {
                $componentName = str_replace($type, '', $componentName);
            }

            $columnName = Str::snake($componentName);

            $components[$columnName] = [
                'type' => '\\' . $className,
                'constructor' => 'create',
                'methods' => [],
                'component_type' => 'custom',
            ];
        }

        return $components;
    }

    /**
     * Generate resource form schema from a model.
     *
     * @param  class-string<Model>|null  $model
     */
    public function outputFormComponents(?string $model = null, array $exceptColumns = []): string
    {
        if (blank($model) || ! class_exists($model)) {
            return 'return $schema->components([]);';
        }

        $schema = $this->getModelSchema($model);
        $table = $this->getModelTable($model);
        $formComponents = $this->getCustomFormComponents();
        $components = [];

        foreach ($schema->getColumns($table) as $column) {
            if ($column['auto_increment']) {
                continue;
            }

            $columnName = $column['name'];

            // Skip default ignored columns
            if (str($columnName)->is([
                app($model)->getKeyName(),
                'created_at', 'updated_at', 'deleted_at', '*_token',
                ...$exceptColumns,
            ])) {
                continue;
            }

            $type = $this->parseColumnType($column);
            $componentData = [];

            // Use custom component if exists
            if (isset($formComponents[$columnName])) {
                $customComponent = $formComponents[$columnName];
                $componentData['type'] = $customComponent['type'];
                $componentData[$customComponent['constructor']] = [];
                if (isset($customComponent['component_type'])) {
                    $componentData['component_type'] = $customComponent['component_type'];
                }
                foreach ($customComponent['methods'] as $method => $params) {
                    $componentData[$method] = $params;
                }
            } else {
                // Default mapping
                $componentData['type'] = match (true) {
                    $type['name'] === 'boolean' => Toggle::class,
                    $type['name'] === 'date' => DatePicker::class,
                    in_array($type['name'], ['datetime', 'timestamp']) => DateTimePicker::class,
                    $type['name'] === 'text' => Textarea::class,
                    str($columnName)->contains(['image', '_image']) => FileUpload::class,
                    default => TextInput::class,
                };

                // Handle relationships
                if (str($columnName)->endsWith('_id') || str($columnName)->endsWith('_by')) {
                    $guessedRelationshipName = $this->guessBelongsToRelationshipName($columnName, $model);

                    if (filled($guessedRelationshipName)) {
                        $relatedModelClass = app($model)->{$guessedRelationshipName}()->getModel()::class;
                        $relatedTitleColumn = $this->guessBelongsToRelationshipTitleColumnName($columnName, $relatedModelClass);

                        if (isset($formComponents[$guessedRelationshipName])) {
                            $componentData['type'] = $formComponents[$guessedRelationshipName]['type'];
                            $componentData['create'] = 'create';
                            $componentData['component_type'] = 'custom';
                        } else {
                            $componentData['type'] = Select::class;
                            $componentData['relationship'] = [$guessedRelationshipName, $relatedTitleColumn];
                        }
                    }
                }
            }

            // Apply rules
            if (! $column['nullable']) {
                $componentData['required'] = [];
            }

            if ($componentData['type'] === TextInput::class) {
                if (str($columnName)->contains('email')) {
                    $componentData['email'] = [];
                }
                if (str($columnName)->contains('password')) {
                    $componentData['password'] = [];
                }
                if (str($columnName)->contains(['phone', 'tel'])) {
                    $componentData['tel'] = [];
                }
            }

            if ($componentData['type'] === FileUpload::class) {
                $componentData['image'] = [];
            }

            if (isset($type['length']) && in_array($componentData['type'], [TextInput::class, Textarea::class])) {
                $componentData['maxLength'] = [$type['length']];
            }

            $components[$columnName] = $componentData;
        }

        if (empty($components)) {
            return 'return $schema->components([]);';
        }

        // Build PHP output for components
        $componentsOutput = '';
        foreach ($components as $columnName => $componentData) {
            $componentClass = $componentData['type'];

            // Use fully qualified namespace for non-custom components, keep custom components as is
            $componentOutput = isset($componentData['component_type'])
                ? $componentClass
                : '\\' . $componentClass;

            $componentOutput .= isset($componentData['create']) ? '::create()' : '::make(\'' . $columnName . '\')';

            unset($componentData['type'], $componentData['create'], $componentData['component_type']);

            foreach ($componentData as $method => $params) {
                $componentOutput .= '->' . $method . '('
                    . collect($params)
                        ->map(fn ($p) => is_string($p) ? "'{$p}'" : (is_bool($p) ? ($p ? 'true' : 'false') : $p))
                        ->implode(', ')
                    . ')';
            }

            $componentsOutput .= '                    ' . $componentOutput . ',' . PHP_EOL;
        }

        // Wrap components in the Group and Section structure
        $output = <<<PHP
return \$schema->components([
    \\Filament\\Schemas\\Components\\Group::make()
        ->schema([
            \\Filament\\Schemas\\Components\\Section::make()
                ->schema([
$componentsOutput
                ])
                ->columns(12)
                ->columnSpan(12),
        ])
        ->columns(12)
        ->columnSpan(8),
    \\Filament\\Schemas\\Components\\Group::make()
        ->schema([
            \\Filament\\Schemas\\Components\\Section::make()
                ->schema([])
                ->columns(12)
                ->columnSpan(12),
        ])
        ->columns(12)
        ->columnSpan(4),
])
    ->columns(12);
PHP;

        return $output;
    }
}
