<?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\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use ReflectionClass;

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

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

        $files = File::files($formComponentsPath);

        foreach ($files 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;
            }

            // Map component to column name (e.g., StatusSelect -> status_id)
            $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', // Assume create() method
                'methods' => [],
                'component_type' => 'custom',
            ];
        }

        return $components;
    }

    protected function getResourceFormSchema(string $model): string
    {
        $model = $this->getModel($model);

        if (blank($model)) {
            return '//';
        }

        $schema = $this->getModelSchema($model);
        $table = $this->getModelTable($model);

        $components = [];
        $formComponents = $this->getFormComponents();

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

            $columnName = $column['name'];

            if (str($columnName)->is([
                app($model)->getKeyName(),
                'created_at',
                'deleted_at',
                'updated_at',
                '*_token',
            ])) {
                continue;
            }

            $type = $this->parseColumnType($column);

            $componentData = [];

            // Check for custom form component
            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 component 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,
                    $columnName === 'image' || str($columnName)->startsWith('image_') || str($columnName)->contains('_image_') || str($columnName)->endsWith('_image') => FileUpload::class,
                    default => TextInput::class,
                };
                if (str($columnName)->endsWith('_id') || str($columnName)->endsWith('_by')) {
                    $standardRelationName = explode('_', $columnName)[0];
                    $guessedRelationshipName = $this->guessBelongsToRelationshipName($columnName, $model);

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

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

            if (in_array($columnName, [
                'id',
                'sku',
                'uuid',
            ])) {
                $componentData['label'] = [Str::upper($columnName)];
            }

            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 (! $column['nullable']) {
                $componentData['required'] = [];
            }

            if (in_array($type['name'], [
                'integer',
                'decimal',
                'float',
                'double',
                'money',
            ])) {
                if ($componentData['type'] === TextInput::class) {
                    $componentData['numeric'] = [];
                }

                if (filled($column['default'])) {
                    $componentData['default'] = [$this->parseDefaultExpression($column, $model)];
                }

                if (in_array($columnName, [
                    'cost',
                    'money',
                    'price',
                ]) || $type['name'] === 'money') {
                    if (! isset($componentData['component_type'])) {
                        $componentData['prefix'] = ['تومان'];
                    }
                }
            } elseif (in_array($componentData['type'], [
                TextInput::class,
                Textarea::class,
            ]) && isset($type['length'])) {
                $componentData['maxLength'] = [$type['length']];

                if (filled($column['default'])) {
                    $componentData['default'] = [$this->parseDefaultExpression($column, $model)];
                }
            }

            if (! isset($componentData['component_type'])) {
                $componentData['columnSpan'] = '3';
            }

            if ($componentData['type'] === Textarea::class) {
                $componentData['columnSpanFull'] = [];
            }

            $components[$columnName] = $componentData;
        }

        $output = count($components) ? '' : '//';

        foreach ($components as $componentName => $componentData) {
            // Constructor
            $output .= PHP_EOL;
            $componentClass = $componentData['type'];
            // Log::info($componentData);
            if (! isset($componentData['component_type'])) {
                $output .= (string) str($componentClass)->after('Filament\\');
            } else {
                $output .= (string) str($componentClass);
            }

            if (isset($componentData['create'])) {
                if ($componentName == 'status') {
                    $output .= '::create(' . class_basename($model) . '::class)';
                } else {
                    $output .= '::create()';
                }
            } else {
                $output .= '::make(\'' . $componentName . '\')';
            }

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

            // Configuration
            foreach ($componentData as $methodName => $parameters) {
                $output .= '->';
                $output .= $methodName;
                $output .= '(';
                $output .= collect($parameters)
                    ->map(function (mixed $parameterValue, int|string $parameterName): string {
                        $parameterValue = match (true) {
                            /** @phpstan-ignore-next-line */
                            is_bool($parameterValue) => $parameterValue ? 'true' : 'false',
                            /** @phpstan-ignore-next-line */
                            is_null($parameterValue) => 'null',
                            is_numeric($parameterValue) => $parameterValue,
                            default => "'{$parameterValue}'",
                        };

                        if (is_numeric($parameterName)) {
                            return $parameterValue;
                        }

                        return "{$parameterName}: {$parameterValue}";
                    })
                    ->implode(', ');
                $output .= ')';
            }

            // Termination
            $output .= ',';

            if ((array_key_last($components) === $componentName)) {
                $output .= PHP_EOL;
            }
        }

        return $output;
    }
}
