<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Mtc\Crm\Models\Form;

class SubmitEnquiryRequest extends FormRequest
{
    /** @var Form $form */
    protected $form;

    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return array_merge([
            'id' => [
                'required',
                'numeric',
                'exists:forms,id',
            ],
            'questions' => [
                'required',
                'array',
            ],
            'source' => [
                'nullable',
                'string'
            ],
        ], $this->questionSpecificRules());
    }

    #[\Override]
    public function attributes()
    {
        if (empty($this->form())) {
            // no form found - no question rules. will fail form validation
            return [];
        }

        return $this->form()
            ->questions
            ->keyBy(fn ($question) => $this->questionRuleKey($question))
            ->map(fn ($question) => $question->name)
            ->filter()
            ->toArray();
    }

    /**
     * Custom validation messages.
     *
     * The default "The :attribute must be a file." reads awkwardly for a file field (especially a
     * multiple one), so file uploads get a clearer message from the validation.enquiry.* lang
     * lines. :attribute still resolves to the question name via attributes().
     *
     * @return array
     */
    #[\Override]
    public function messages()
    {
        if (empty($this->form())) {
            return [];
        }

        return $this->form()
            ->questions
            ->filter(fn ($question) => $question->type === 'file_upload')
            ->mapWithKeys(function ($question) {
                $message = ($question->data['allow_multiple'] ?? false)
                    ? __('validation.enquiry.file_multiple')
                    : __('validation.enquiry.file_single');

                return ["{$this->questionRuleKey($question)}.file" => $message];
            })
            ->toArray();
    }

    protected function prepareForValidation()
    {
        if (!$this->filled('questions') && $this->filled('sections')) {
            $this->merge([
                'questions' => collect($this->input('sections'))
                    ->pluck('segments')
                    ->flatten(1)
                    ->pluck('questions')
                    ->flatten(1)
                    ->keyBy('id')
                    ->map(fn ($question) => $question['value'] ?? null)
                    ->toArray()
            ]);
        }
    }

    /**
     * @return array
     */
    protected function questionSpecificRules(): array
    {
        if (empty($this->form())) {
            // no form found - no question rules. will fail form validation
            return [];
        }

        return $this->form()
            ->questions
            ->keyBy(fn ($question) => $this->questionRuleKey($question))
            ->map(fn ($question) => $question->validation)
            ->filter()
            ->toArray();
    }

    /**
     * The request input key a question's answer is validated (and named) under.
     *
     * File uploads for a question with allow_multiple arrive as an array (files[<id>][]), so each
     * element is validated individually via the wildcard key. Single uploads and other question
     * types use a flat key. Centralised so the rule keys and the attribute names always agree.
     *
     * @param mixed $question
     * @return string
     */
    protected function questionRuleKey($question): string
    {
        if ($question->type !== 'file_upload') {
            return "questions.$question->id";
        }

        return ($question->data['allow_multiple'] ?? false)
            ? "files.$question->id.*"
            : "files.$question->id";
    }

    /**
     * Get the form object from request
     *
     * @return Form|null
     */
    public function form(): ?Form
    {
        if (!tenant()) {
            return null;
        }

        if (empty($this->form)) {
            $this->form = Form::query()
                ->with('questions')
                ->find($this->input('id'));
        }

        return $this->form;
    }
}
