<?php

namespace App\Http\Requests\Api\V1;

use App\Models\Environments\Environment;
use App\Services\Api\ApiResponse;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Exceptions\HttpResponseException;

class MoodleApiRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        return true; // La autorización se maneja en el middleware ProductToken
    }

    /**
     * Get the validation rules that apply to the request.
     */
    public function rules(): array
    {
        $action = $this->input('action');

        $rules = [
            'action' => ['required', 'string', 'in:sync,licence,products,scss,scss-cdn,js,setup,features,tutorials,resources,data,plugins'],
            'host' => ['required', 'string', 'url', 'max:500'],
            'projectid' => ['nullable', 'string', 'max:255'],
        ];

        // Para sync, site/environment.version y release son opcionales; para el resto required_without
        if ($action === 'sync') {
            $rules['environment.version'] = ['nullable', 'string', 'max:100'];
            $rules['environment.release'] = ['nullable', 'string', 'max:100'];
            $rules['site.version'] = ['nullable', 'string', 'max:100'];
            $rules['site.release'] = ['nullable', 'string', 'max:100'];
        } else {
            $rules['environment.version'] = ['required_without:site.version', 'string', 'max:100'];
            $rules['environment.release'] = ['required_without:site.release', 'string', 'max:100'];
            $rules['site.version'] = ['required_without:environment.version', 'string', 'max:100'];
            $rules['site.release'] = ['required_without:environment.release', 'string', 'max:100'];
        }

        $rules['environment.lastversion'] = ['nullable', 'string', 'max:100'];
        $rules['environment.lastminor'] = ['nullable', 'string', 'max:100'];
        $rules['environment.token'] = ['nullable', 'string', 'max:500'];
        $rules['environment.type'] = ['nullable', 'string', 'in:moodle,workplace,goodle,wordpress,other'];
        // **La lista sale del catálogo de usos, no escrita a mano.** `env` llega en el
        // payload de cada Moodle y esta regla es la única puerta: con la lista fija, un uso
        // nuevo dado de alta en `Configuración › Catálogos` se rechazaba aquí con un 422
        // que hablaba de un campo válido. Ver `Env` y `Environment::reglaDeEnv()`.
        $usos = implode(',', Environment::entornos());

        $rules['environment.env'] = ['nullable', 'string', 'in:' . $usos];
        $rules['environment.key'] = ['nullable', 'string', 'max:255'];
        $rules['environment.availableupdates'] = ['nullable', 'array'];
        $rules['environment.availableupdatesfetch'] = ['nullable'];
        $rules['site.lastversion'] = ['nullable', 'string', 'max:100'];
        $rules['site.lastminor'] = ['nullable', 'string', 'max:100'];
        $rules['site.token'] = ['nullable', 'string', 'max:500'];
        $rules['site.type'] = ['nullable', 'string', 'in:moodle,workplace,goodle,wordpress,other'];
        $rules['site.env'] = ['nullable', 'string', 'in:' . $usos];
        $rules['site.key'] = ['nullable', 'string', 'max:255'];
        $rules['site.availableupdates'] = ['nullable', 'array'];
        $rules['site.availableupdatesfetch'] = ['nullable'];

        // Para la acción 'products', plugin y version son opcionales
        if ($action === 'products') {
            $rules['plugin'] = ['nullable', 'string', 'max:255'];
            $rules['version'] = ['nullable', 'string', 'regex:/^\d{10}$/']; // YYYYMMDDXX
        } else {
            $rules['plugin'] = ['required', 'string', 'max:255'];
            $rules['version'] = ['required', 'string', 'regex:/^\d{10}$/']; // YYYYMMDDXX
        }

        // El campo data es obligatorio para data, plugins y sync
        if (in_array($action, ['data', 'plugins', 'sync'])) {
            $rules['data'] = ['required', 'array'];
            if ($action === 'sync') {
                $rules['data.plugins'] = ['required', 'array'];
            }
        } else {
            $rules['data'] = ['nullable', 'array'];
        }

        // `date` viaja en el payload de la tarea diaria del plugin
        // (`sync_stats_task` → `tip::stats()`), que envía `action = 'data'`. Se acepta
        // pero no se exige: `DataAction` guarda el estado actual y no usa la fecha.
        // Ver api-contract.md, retirada de la acción `stats`.
        // **Sin `date_format` a propósito.** El plugin manda el día sin cero delante
        // —`2026-09-4`—, y con `date_format:Y-m-d` eso devolvía un 422 que **tiraba el
        // payload entero**: 38 claves con el perfil del sitio y sus conteos, perdidas
        // cada día en cada sitio. Rechazar la telemetría de un cliente por un cero de
        // relleno es lo contrario de la norma de retrocompatibilidad de la release.
        //
        // `date` a secas acepta las dos formas, y `DataAction::fechaDeLaFoto()` ya
        // estaba escrito para normalizarla —y para sobrevivir a una fecha ilegible—.
        // Ver MGR-086.
        $rules['date'] = ['nullable', 'date'];

        return $rules;
    }

    /**
     * Get custom messages for validator errors.
     */
    public function messages(): array
    {
        return [
            'action.required' => 'El campo action es obligatorio.',
            'action.in' => 'El campo action debe ser uno de: sync, licence, products, scss, scss-cdn, js, setup, features, tutorials, resources, data, plugins, stats.',
            'plugin.required' => 'El campo plugin es obligatorio.',
            'host.required' => 'El campo host es obligatorio.',
            'host.url' => 'El campo host debe ser una URL válida.',
            'version.required' => 'El campo version es obligatorio.',
            'version.regex' => 'El campo version debe tener el formato YYYYMMDDXX (10 dígitos).',
            'environment.version.required_without' => 'El campo environment.version o site.version es obligatorio.',
            'environment.release.required_without' => 'El campo environment.release o site.release es obligatorio.',
            'site.version.required_without' => 'El campo site.version o environment.version es obligatorio.',
            'site.release.required_without' => 'El campo site.release o environment.release es obligatorio.',
            'environment.type.in' => 'El campo environment.type debe ser uno de: moodle, workplace, goodle, wordpress, other.',
            'environment.env.in' => 'El campo environment.env debe ser uno de: pro, pre, develop, local.',
            'site.type.in' => 'El campo site.type debe ser uno de: moodle, workplace, goodle, wordpress, other.',
            'site.env.in' => 'El campo site.env debe ser uno de: pro, pre, develop, local.',
            'data.required' => 'El campo data es obligatorio para la acción seleccionada.',
            'data.array' => 'El campo data debe ser un array.',
            'data.plugins.required' => 'El campo data.plugins es obligatorio para la acción sync.',
            'data.plugins.array' => 'El campo data.plugins debe ser un array.',
            'date.required' => 'El campo date es obligatorio para la acción stats.',
            'date.date' => 'El campo date no es una fecha reconocible.',
        ];
    }

    /**
     * Maneja errores de validación devolviendo JSON en formato estándar de la API
     *
     * @param Validator $validator
     * @return void
     * @throws HttpResponseException
     */
    protected function failedValidation(Validator $validator): void
    {
        $errors = $validator->errors()->all();
        $firstError = $errors[0] ?? 'Error de validación';

        throw new HttpResponseException(
            ApiResponse::error(
                $this->input('action', 'unknown'),
                $firstError,
                400,
                ['errors' => $errors],
                422
            )
        );
    }
}

