<?php

declare(strict_types=1);

namespace Maestro\Core\Http\Requests\DataTables;

use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Validator;

final class FetchDataTableRequest extends FormRequest
{
    private const MAX_PAYLOAD_BYTES = 51200; // 50KB

    public function authorize(): bool
    {
        return $this->user() !== null;
    }

    /**
     * @return array<string, ValidationRule|array<mixed>|string>
     */
    public function rules(): array
    {
        return [
            'tableKey' => ['nullable', 'string', 'max:150'],
            'pagination' => ['required', 'array'],
            'pagination.page' => ['required', 'integer', 'min:1'],
            'pagination.limit' => ['required', 'integer', 'min:1', 'max:200'],
            'sort' => ['nullable', 'array', 'max:3'],
            'sort.*.field' => ['required', 'string', 'max:64'],
            'sort.*.order' => ['required', 'integer', 'in:1,-1'],
            'visibleColumns' => ['nullable', 'array'],
            'visibleColumns.*' => ['string', 'max:64'],
            'filters' => ['nullable', 'array'],
            'filters.*' => ['array'],
            'filters.*.op' => [
                'nullable',
                'string',
                Rule::in([
                    'contains',
                    'starts_with',
                    'ends_with',
                    'equals',
                    'not_equals',
                    'lt',
                    'lte',
                    'gt',
                    'gte',
                    'date_equals',
                    'before',
                    'after',
                    'between',
                ]),
            ],
            'filters.*.value' => [
                'required',
                function (string $attribute, mixed $value, \Closure $fail): void {
                    if (is_object($value)) {
                        $fail('Filter values must be scalar (string, int, bool, null) or an array of scalars.');

                        return;
                    }
                    if (is_array($value)) {
                        foreach ($value as $item) {
                            if (is_array($item) || is_object($item)) {
                                $fail('Filter values must be scalar (string, int, bool, null) or an array of scalars.');

                                return;
                            }
                        }
                    }
                },
            ],
            'globalSearch' => ['nullable', 'string', 'max:200'],
        ];
    }

    public function withValidator(Validator $validator): void
    {
        $validator->after(function (Validator $validator): void {
            $content = $this->getContent();
            if ($content !== false && strlen($content) > self::MAX_PAYLOAD_BYTES) {
                $validator->errors()->add(
                    'body',
                    'The request payload must not exceed '.(self::MAX_PAYLOAD_BYTES / 1024).' KB.'
                );
            }
        });
    }

    /**
     * @return array<string, mixed>
     */
    public function receivedPayload(): array
    {
        return [
            'pagination' => [
                'page' => (int) $this->input('pagination.page'),
                'limit' => (int) $this->input('pagination.limit'),
            ],
            'sort' => $this->input('sort'),
            'visibleColumns' => $this->input('visibleColumns'),
            'filters' => $this->input('filters'),
            'globalSearch' => $this->input('globalSearch'),
        ];
    }
}
