<?php

namespace Daylight\Core\Modules\FormBuilder\Livewire;

use Daylight\Core\Modules\FormBuilder\Models\Form;
use Daylight\Core\Modules\FormBuilder\Models\FormSubmission;
use Daylight\Core\Modules\FormBuilder\Notifications\FormAutoReplyNotification;
use Daylight\Core\Modules\FormBuilder\Notifications\FormSubmissionNotification;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\View\View;
use Livewire\Component;
use Livewire\WithFileUploads;
use Spatie\Honeypot\Http\Livewire\Concerns\HoneypotData;
use Spatie\Honeypot\Http\Livewire\Concerns\UsesSpamProtection;

class FormRenderer extends Component
{
    use UsesSpamProtection;
    use WithFileUploads;

    public ?Form $form = null;

    public array $formData = [];

    public array $fileUploads = [];

    public bool $submitted = false;

    public HoneypotData $extraFields;

    public function mount(int $formId): void
    {
        $this->extraFields = new HoneypotData;
        $this->form = Form::active()->find($formId);

        if ($this->form) {
            foreach ($this->form->fields as $field) {
                if ($field['type'] === 'heading') {
                    continue;
                }

                $this->formData[$field['id']] = $field['type'] === 'checkbox' ? false : '';
            }
        }
    }

    public function submit(): void
    {
        if (! $this->form || ! $this->form->is_active) {
            return;
        }

        $key = 'form-submission:' . $this->form->id . ':' . request()->ip();

        if (RateLimiter::tooManyAttempts($key, 3)) {
            $this->addError('form', __('daylight::forms.rate_limit_exceeded'));

            return;
        }

        RateLimiter::hit($key, 60);

        $this->protectAgainstSpam();

        $this->validateFormData();

        $visibleFieldIds = $this->getVisibleFieldIds();
        $files = $this->uploadFiles($visibleFieldIds);
        $cleanedData = array_intersect_key($this->formData, array_flip($visibleFieldIds));

        $submission = FormSubmission::create([
            'form_id' => $this->form->id,
            'data' => $cleanedData,
            'files' => ! empty($files) ? $files : null,
            'ip_address' => request()->ip(),
            'page_url' => request()->header('Referer'),
        ]);

        $recipients = array_filter(array_map('trim', explode(',', $this->form->recipient_email)));

        Notification::route('mail', $recipients)
            ->notify(new FormSubmissionNotification($this->form, $submission));

        if ($this->form->auto_reply_enabled) {
            $emailFieldValue = $this->findFirstEmailFieldValue();

            if ($emailFieldValue) {
                Notification::route('mail', $emailFieldValue)
                    ->notify(new FormAutoReplyNotification($this->form, $submission));
            }
        }

        if ($this->form->success_type === 'redirect' && $this->form->success_redirect_url) {
            $this->redirect($this->form->success_redirect_url);

            return;
        }

        $this->submitted = true;
    }

    protected function validateFormData(): void
    {
        $rules = [];
        $messages = [];
        $visibleFieldIds = $this->getVisibleFieldIds();

        foreach ($this->form->fields as $field) {
            if ($field['type'] === 'heading') {
                continue;
            }

            if (! in_array($field['id'], $visibleFieldIds)) {
                continue;
            }

            $fieldRules = [];
            $fieldKey = "formData.{$field['id']}";

            if ($field['type'] === 'checkbox') {
                $fieldRules[] = ($field['required'] ?? false) ? 'accepted' : 'nullable';
            } elseif ($field['required'] ?? false) {
                $fieldRules[] = 'required';
            } else {
                $fieldRules[] = 'nullable';
            }

            $label = $field['label'] ?: $field['type'];

            match ($field['type']) {
                'email' => $fieldRules[] = 'email',
                'number' => $this->applyNumberRules($fieldRules, $field),
                'date' => $fieldRules[] = 'date',
                'file', 'checkbox' => null,
                default => null,
            };

            if (! empty($fieldRules)) {
                $rules[$fieldKey] = $fieldRules;
            }

            $messages["{$fieldKey}.required"] = __('daylight::forms.validation.field_required', ['field' => $label]);
            $messages["{$fieldKey}.accepted"] = __('daylight::forms.validation.field_required', ['field' => $label]);
            $messages["{$fieldKey}.email"] = __('daylight::forms.validation.field_email', ['field' => $label]);
            $messages["{$fieldKey}.numeric"] = __('daylight::forms.validation.field_numeric', ['field' => $label]);
            $messages["{$fieldKey}.min"] = __('daylight::forms.validation.field_min', ['field' => $label, 'min' => $field['min'] ?? '']);
            $messages["{$fieldKey}.max"] = __('daylight::forms.validation.field_max', ['field' => $label, 'max' => $field['max'] ?? '']);
            $messages["{$fieldKey}.date"] = __('daylight::forms.validation.field_date', ['field' => $label]);
        }

        foreach ($this->form->fields as $field) {
            if ($field['type'] !== 'file') {
                continue;
            }

            if (! in_array($field['id'], $visibleFieldIds)) {
                continue;
            }

            $fileKey = "fileUploads.{$field['id']}";
            $fileRules = ['nullable', 'file', 'max:10240'];

            if ($field['required'] ?? false) {
                $fileRules[0] = 'required';
            }

            $rules[$fileKey] = $fileRules;

            $label = $field['label'] ?: __('daylight::forms.file');
            $messages["{$fileKey}.required"] = __('daylight::forms.validation.field_required', ['field' => $label]);
            $messages["{$fileKey}.max"] = __('daylight::forms.validation.field_max_file', ['field' => $label]);
        }

        $this->validate($rules, $messages);
    }

    protected function applyNumberRules(array &$rules, array $field): void
    {
        $rules[] = 'numeric';

        if (isset($field['min']) && $field['min'] !== null && $field['min'] !== '') {
            $rules[] = "min:{$field['min']}";
        }

        if (isset($field['max']) && $field['max'] !== null && $field['max'] !== '') {
            $rules[] = "max:{$field['max']}";
        }
    }

    protected function uploadFiles(array $visibleFieldIds): array
    {
        $files = [];

        foreach ($this->form->fields as $field) {
            if ($field['type'] !== 'file') {
                continue;
            }

            if (! in_array($field['id'], $visibleFieldIds)) {
                continue;
            }

            $upload = $this->fileUploads[$field['id']] ?? null;

            if ($upload) {
                $path = $upload->store('form-submissions/' . $this->form->id, 'media');
                $files[$field['id']] = [$path];
            }
        }

        return $files;
    }

    protected function getVisibleFieldIds(): array
    {
        $visibleIds = [];

        foreach ($this->form->fields as $field) {
            if ($field['type'] === 'heading') {
                continue;
            }

            if ($this->isFieldVisible($field)) {
                $visibleIds[] = $field['id'];
            }
        }

        return $visibleIds;
    }

    protected function isFieldVisible(array $field): bool
    {
        $condition = $field['condition'] ?? null;

        if (! $condition || empty($condition['field_id'])) {
            return true;
        }

        $sourceValue = $this->formData[$condition['field_id']] ?? null;

        return match ($condition['operator'] ?? 'equals') {
            'equals' => (string) $sourceValue === (string) $condition['value'],
            'not_equals' => (string) $sourceValue !== (string) $condition['value'],
            'is_empty' => $sourceValue === null || $sourceValue === '' || $sourceValue === false,
            'is_not_empty' => $sourceValue !== null && $sourceValue !== '' && $sourceValue !== false,
            default => true,
        };
    }

    protected function findFirstEmailFieldValue(): ?string
    {
        foreach ($this->form->fields as $field) {
            if ($field['type'] === 'email') {
                $value = $this->formData[$field['id']] ?? null;

                if ($value && filter_var($value, FILTER_VALIDATE_EMAIL)) {
                    return $value;
                }
            }
        }

        return null;
    }

    public function render(): View
    {
        return view('daylight::livewire.form-builder.form-renderer');
    }
}
