<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Form;
use App\Models\FormField;
use App\Support\ActivityLogger;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\View\View;

class FormController extends Controller
{
    private const MAX_GROUP_DEPTH = 4;

    public function index(): View
    {
        $forms = Form::withCount('fields', 'filiales')->latest()->paginate(10);

        return view('admin.forms.index', compact('forms'));
    }

    public function create(): View
    {
        $this->authorize('create', Form::class);

        return view('admin.forms.create', [
            'fieldTypes' => FormField::TYPES,
        ]);
    }

    public function store(Request $request): RedirectResponse
    {
        $this->authorize('create', Form::class);

        $validated = $this->validateForm($request);

        $form = Form::create([
            'title' => $validated['title'],
            'description' => $validated['description'] ?? null,
            'created_by' => auth()->id(),
        ]);

        $this->syncFields($form, $validated['fields']);

        ActivityLogger::log('form.created', $form, $form->title);

        return redirect()->route('admin.forms.index')
            ->with('success', 'Formulaire créé avec succès.');
    }

    public function show(Form $form): View
    {
        $form->load(['filiales']);
        $tree = $form->fieldsTree();

        return view('admin.forms.show', compact('form', 'tree'));
    }

    public function edit(Form $form): View
    {
        $this->authorize('update', $form);

        return view('admin.forms.edit', [
            'form' => $form,
            'fieldTypes' => FormField::TYPES,
            'initialFields' => $this->buildInitialFields($form->fieldsTree()),
        ]);
    }

    public function update(Request $request, Form $form): RedirectResponse
    {
        $this->authorize('update', $form);

        $validated = $this->validateForm($request, true);

        DB::transaction(function () use ($form, $validated, $request) {
            $form->update([
                'title' => $validated['title'],
                'description' => $validated['description'] ?? null,
                'is_active' => $request->boolean('is_active', true),
            ]);

            $keptIds = [];
            $this->syncFieldsDiff($form, $validated['fields'], null, $keptIds);

            // Supprime uniquement les champs retirés par l'admin (leurs réponses sont alors effacées).
            $form->fields()->whereNotIn('id', $keptIds)->delete();
        });

        ActivityLogger::log('form.updated', $form, $form->title);

        return redirect()->route('admin.forms.index')
            ->with('success', 'Formulaire mis à jour.');
    }

    public function destroy(Form $form): RedirectResponse
    {
        $this->authorize('delete', $form);

        $title = $form->title;
        $form->delete();

        ActivityLogger::log('form.deleted', $form, $title);

        return redirect()->route('admin.forms.index')
            ->with('success', 'Formulaire supprimé.');
    }

    /**
     * Verrouille / déverrouille la modification du formulaire côté filiales.
     */
    public function toggleLock(Form $form): RedirectResponse
    {
        $this->authorize('operate', $form);

        $form->update(['is_active' => ! $form->is_active]);

        $locked = ! $form->is_active;

        ActivityLogger::log(
            $locked ? 'form.locked' : 'form.unlocked',
            $form,
            $form->title
        );

        return back()->with(
            'success',
            $locked
                ? 'Modifications désactivées : les filiales ne peuvent plus modifier leurs réponses.'
                : 'Modifications réactivées : les filiales peuvent à nouveau modifier leurs réponses.'
        );
    }

    private function validateForm(Request $request, bool $isUpdate = false): array
    {
        $rules = [
            'title' => ['required', 'string', 'max:255'],
            'description' => ['nullable', 'string'],
            'fields' => ['required', 'array', 'min:1'],
        ];

        // Genere les regles pour chaque niveau d'imbrication (jusqu'a MAX_GROUP_DEPTH).
        $this->addFieldRules($rules, 'fields', 0);

        if ($isUpdate) {
            $rules['is_active'] = ['boolean'];
        }

        $validated = $request->validate($rules);

        $this->validateFieldStructure($validated['fields']);

        return $validated;
    }

    private function addFieldRules(array &$rules, string $prefix, int $depth): void
    {
        $rules["{$prefix}.*.id"] = ['nullable', 'integer'];
        $rules["{$prefix}.*.label"] = ['required', 'string', 'max:255'];
        $rules["{$prefix}.*.description"] = ['nullable', 'string', 'max:1000'];
        $rules["{$prefix}.*.type"] = ['required', Rule::in(FormField::TYPES)];
        $rules["{$prefix}.*.options"] = ['nullable', 'string'];
        $rules["{$prefix}.*.required"] = ['nullable', 'boolean'];
        $rules["{$prefix}.*.columns"] = ['nullable', 'array'];
        $rules["{$prefix}.*.columns.*.label"] = ['required_with:'.$prefix.'.*.columns', 'string', 'max:255'];
        $rules["{$prefix}.*.columns.*.type"] = ['required_with:'.$prefix.'.*.columns', Rule::in(FormField::TABLE_COLUMN_TYPES)];
        $rules["{$prefix}.*.columns.*.options"] = ['nullable', 'string'];

        if ($depth < self::MAX_GROUP_DEPTH) {
            $rules["{$prefix}.*.children"] = ['nullable', 'array'];
            $this->addFieldRules($rules, "{$prefix}.*.children", $depth + 1);
        }
    }

    private function validateFieldStructure(array $fields, string $path = 'fields'): void
    {
        foreach ($fields as $index => $field) {
            if (($field['type'] ?? null) === 'table') {
                if (empty($field['columns']) || count($field['columns']) < 1) {
                    throw \Illuminate\Validation\ValidationException::withMessages([
                        "{$path}.{$index}.columns" => 'Un tableau doit avoir au moins une colonne.',
                    ]);
                }
            }

            if (($field['type'] ?? null) === 'group' && ! empty($field['children']) && is_array($field['children'])) {
                $this->validateFieldStructure($field['children'], "{$path}.{$index}.children");
            }
        }
    }

    private function syncFields(Form $form, array $fields, ?int $parentId = null): void
    {
        foreach ($fields as $index => $fieldData) {
            $nameKey = Str::slug($fieldData['label'] ?? '', '_');
            $nameKey = $nameKey === '' ? 'field' : $nameKey;

            $field = $form->fields()->create([
                'parent_id' => $parentId,
                'label' => $fieldData['label'],
                'description' => trim($fieldData['description'] ?? '') !== '' ? $fieldData['description'] : null,
                'name_key' => $nameKey.'_'.($parentId ?? 0).'_'.$index,
                'type' => $fieldData['type'],
                'options' => $this->resolveFieldOptions($fieldData),
                'required' => ! empty($fieldData['required']),
                'order' => $index,
            ]);

            if ($fieldData['type'] === 'group' && ! empty($fieldData['children']) && is_array($fieldData['children'])) {
                $this->syncFields($form, $fieldData['children'], $field->id);
            }
        }
    }

    /**
     * Mise à jour non destructive : conserve les champs existants (et donc leurs réponses)
     * en les identifiant par leur id, crée les nouveaux, et collecte les ids conservés.
     */
    private function syncFieldsDiff(Form $form, array $fields, ?int $parentId, array &$keptIds): void
    {
        foreach ($fields as $index => $fieldData) {
            $existingId = isset($fieldData['id']) && $fieldData['id'] !== null && $fieldData['id'] !== ''
                ? (int) $fieldData['id']
                : null;

            $attributes = [
                'parent_id' => $parentId,
                'label' => $fieldData['label'],
                'description' => trim($fieldData['description'] ?? '') !== '' ? $fieldData['description'] : null,
                'type' => $fieldData['type'],
                'options' => $this->resolveFieldOptions($fieldData),
                'required' => ! empty($fieldData['required']),
                'order' => $index,
            ];

            $field = $existingId !== null
                ? $form->fields()->whereKey($existingId)->first()
                : null;

            if ($field) {
                $field->update($attributes);
            } else {
                $field = $form->fields()->create($attributes + [
                    'name_key' => 'tmp_'.uniqid(),
                ]);
            }

            // name_key stable et unique basé sur l'id définitif.
            if ($field->name_key !== 'f_'.$field->id) {
                $field->update(['name_key' => 'f_'.$field->id]);
            }

            $keptIds[] = $field->id;

            if ($fieldData['type'] === 'group' && ! empty($fieldData['children']) && is_array($fieldData['children'])) {
                $this->syncFieldsDiff($form, $fieldData['children'], $field->id, $keptIds);
            }
        }
    }

    private function resolveFieldOptions(array $fieldData): ?array
    {
        if ($fieldData['type'] === 'group') {
            return null;
        }

        if ($fieldData['type'] === 'table') {
            $columns = [];
            foreach ($fieldData['columns'] ?? [] as $col) {
                $column = [
                    'label' => $col['label'],
                    'type' => $col['type'],
                ];
                if ($col['type'] === 'select' && ! empty($col['options'])) {
                    $column['options'] = array_values(array_filter(array_map('trim', explode(',', $col['options']))));
                }
                $columns[] = $column;
            }

            return $columns;
        }

        return $this->parseOptions($fieldData['options'] ?? null);
    }

    private function parseOptions(?string $options): ?array
    {
        if (blank($options)) {
            return null;
        }

        return array_values(array_filter(array_map('trim', explode(',', $options))));
    }

    /**
     * Convertit l'arbre des champs en structure attendue par le constructeur Alpine.
     *
     * @param  \Illuminate\Support\Collection|array  $fieldsTree
     */
    private function buildInitialFields($fieldsTree): array
    {
        $result = [];

        foreach ($fieldsTree as $field) {
            $entry = [
                'id' => $field->id,
                'label' => $field->label,
                'description' => $field->description ?? '',
                'type' => $field->type,
                'options' => $field->type !== 'table' && $field->type !== 'group' && is_array($field->options)
                    ? implode(', ', $field->options)
                    : '',
                'required' => (bool) $field->required,
                'columns' => $field->type === 'table'
                    ? array_map(function ($col) {
                        return [
                            'label' => $col['label'] ?? '',
                            'type' => $col['type'] ?? 'text',
                            'options' => isset($col['options']) && is_array($col['options'])
                                ? implode(', ', $col['options'])
                                : ($col['options'] ?? ''),
                        ];
                    }, $field->options ?? [])
                    : [],
                'children' => $field->type === 'group'
                    ? $this->buildInitialFields($field->children)
                    : [],
            ];

            $result[] = $entry;
        }

        return $result;
    }
}
