<?php

namespace Maestro\Core\Http\Requests;

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

/**
 * B4: Generic request validation for the DataTable engine.
 * Guarantees a consistent, safe payload shape for all DataTable resources.
 */
class FetchDataTableRequest extends FormRequest
{
    private const MAX_PAYLOAD_BYTES = 51200; // 50KB

    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        return $this->user() !== null;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, \Illuminate\Contracts\Validation\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([
                    // text / enum / badge
                    'contains',
                    'starts_with',
                    'ends_with',
                    'equals',
                    'not_equals',
                    // number
                    'lt',
                    'lte',
                    'gt',
                    'gte',
                    // date
                    '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'],
        ];
    }

    /**
     * Reject oversized JSON body (payload size limit). 422 if body > 50KB.
     */
    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 sanitized payload for meta.received (no huge data).
     *
     * @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'),
        ];
    }
}
