<?php

declare(strict_types=1);

namespace App\Http\Requests\Pwa;

use App\Models\CollectionVisit;
use App\Models\CompanyFinancialSettings;
use App\Support\SimplifiedAmount;
use Carbon\Carbon;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class StoreCollectionVisitRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        // Scope de empresa en las reglas exists (defensa en profundidad; el
        // controller re-scopea por company_id): no confirmar existencia de
        // credit_id/installment_id de otra empresa.
        $companyId = $this->user()?->company_id;

        return [
            'credit_id' => [
                'required', 'integer',
                Rule::exists('credits', 'id')->where('company_id', $companyId),
            ],
            'installment_id' => [
                'nullable', 'integer',
                Rule::exists('installments', 'id')->where('company_id', $companyId),
            ],
            'visit_type' => ['required', 'string', Rule::in(CollectionVisit::VISIT_TYPES)],
            'reason' => ['nullable', 'string', Rule::in(CollectionVisit::REASONS)],
            'notes' => ['nullable', 'string', 'max:1000'],
            // Las fechas, con un día de margen por la zona horaria del teléfono:
            // desde PWA-002 una fila que el directo rechaza el teléfono la suelta,
            // y el lote (SyncController::syncVisits) la habría aceptado. Más de un
            // día ya no es la zona horaria: acá solo llegan filas recién capturadas.
            'promised_date' => ['nullable', 'date', 'after_or_equal:'.Carbon::yesterday()->toDateString()],
            // min:0 como el lote.
            'promised_amount' => ['nullable', 'numeric', 'min:0', 'max:9999999.99'],
            'visit_date' => ['required', 'date', 'before_or_equal:'.Carbon::tomorrow()->toDateString()],
            'visit_time' => ['nullable', 'date_format:H:i'],
            'latitude' => ['nullable', 'numeric', 'between:-90,90'],
            'longitude' => ['nullable', 'numeric', 'between:-180,180'],
            'idempotency_key' => ['nullable', 'string', 'uuid', 'max:36'],
            'device_id' => ['nullable', 'string', 'max:255'],
            'is_simplified_amount' => ['nullable', 'boolean'],
            'simplified_amount_confirmed' => ['nullable', 'boolean'],
        ];
    }

    /**
     * Prepare the data for validation.
     *
     * Convierte promised_amount de "modo simplificado" (ej. 1500 → $1.500.000)
     * si el ítem trae el flag, igual que el lote: aunque la empresa haya apagado
     * el modo, el flag dice cómo se escribió el número. Conversión opcional:
     * solo si se envía promised_amount. El flag se lee como lo lee el lote:
     * "true" o "on" no son un 422.
     */
    protected function prepareForValidation(): void
    {
        if ($this->has('is_simplified_amount')) {
            $this->merge(['is_simplified_amount' => SimplifiedAmount::flagged($this->all())]);
        }

        if (SimplifiedAmount::applies($this->all(), 'promised_amount')) {
            $settings = CompanyFinancialSettings::forCompany($this->user()->company_id);
            $this->merge(SimplifiedAmount::toReal($this->only('promised_amount', 'is_simplified_amount'), 'promised_amount', $settings));
        }
    }

    public function messages(): array
    {
        return [
            'credit_id.required' => 'El crédito es obligatorio.',
            'credit_id.exists' => 'El crédito no existe.',
            'visit_type.required' => 'El tipo de visita es obligatorio.',
            'visit_type.in' => 'Tipo de visita inválido.',
            'reason.in' => 'Motivo inválido.',
            'notes.max' => 'Las notas no pueden exceder 1000 caracteres.',
            'promised_date.after_or_equal' => 'La fecha prometida no puede ser pasada.',
            'promised_amount.min' => 'El monto prometido no puede ser negativo.',
            'visit_date.required' => 'La fecha de visita es obligatoria.',
            'visit_date.before_or_equal' => 'La fecha de visita no puede ser futura.',
            'visit_time.date_format' => 'Formato de hora inválido (use HH:MM).',
            'latitude.between' => 'Latitud inválida.',
            'longitude.between' => 'Longitud inválida.',
            'idempotency_key.uuid' => 'La clave de idempotencia debe ser un UUID válido.',
        ];
    }
}
