<?php

declare(strict_types=1);

namespace App\Http\Requests\Pwa;

use App\Models\CompanyFinancialSettings;
use Carbon\Carbon;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Validator;

/**
 * Form request para registro de pagos desde PWA.
 *
 * IMPORTANTE: Validación de fecha
 * - Usa timezone de la aplicación (America/Bogota)
 * - Permite pagos retroactivos (fechas pasadas)
 * - Solo bloquea fechas estrictamente futuras (mañana o después)
 * - Compara solo fechas (sin hora) para evitar edge cases de timezone
 */
class StorePaymentRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        // Obtener "hoy" en la timezone de la aplicación
        $today = Carbon::today()->toDateString();

        // Límite inferior para offline_created_at: hace 7 días (pagos muy antiguos son sospechosos)
        $sevenDaysAgo = Carbon::now()->subDays(7)->toDateTimeString();

        // Scope de empresa en la regla exists: evita confirmar la existencia de un
        // credit_id de otra empresa (oráculo de enumeración). Defensa en profundidad:
        // el controller además re-carga el crédito filtrando por company_id.
        $companyId = $this->user()?->company_id;

        return [
            'credit_id' => [
                'required', 'integer',
                Rule::exists('credits', 'id')->where('company_id', $companyId),
            ],
            'amount' => ['required', 'numeric', 'min:0.01', 'max:9999999.99'],
            'payment_method' => ['nullable', 'string', 'in:cash,transfer,mobile'],
            // Usar fecha específica en vez de 'today' para evitar ambigüedad de timezone
            'payment_date' => ['required', 'date', "before_or_equal:{$today}"],
            'notes' => ['nullable', 'string', 'max:500'],
            'idempotency_key' => ['required', 'string', 'uuid', 'max:36'],
            'device_id' => ['nullable', 'string', 'max:255'],
            'is_simplified_amount' => ['nullable', 'boolean'],
            'simplified_amount_confirmed' => ['nullable', 'boolean'],
            // El cobrador ya vio el aviso de un cobro igual del mismo día y dijo
            // que este es otro (ver PaymentController@store).
            'confirm_duplicate' => ['nullable', 'boolean'],
            // Timestamp de creación offline: no puede ser futuro ni tener más de 7 días
            'offline_created_at' => ['nullable', 'date', 'before_or_equal:now', "after:{$sevenDaysAgo}"],
            // Geolocalización GPS (opcional — capturada en el dispositivo)
            'latitude' => ['nullable', 'numeric', 'between:-90,90'],
            'longitude' => ['nullable', 'numeric', 'between:-180,180'],
        ];
    }

    public function messages(): array
    {
        return [
            'credit_id.required' => 'El crédito es obligatorio.',
            'credit_id.exists' => 'El crédito no existe.',
            'amount.required' => 'El monto es obligatorio.',
            'amount.numeric' => 'El monto debe ser un número.',
            'amount.min' => 'El monto mínimo es $0.01.',
            'amount.max' => 'El monto máximo es $9,999,999.99.',
            'payment_method.in' => 'Método de pago inválido.',
            'payment_date.required' => 'La fecha de pago es obligatoria.',
            'payment_date.date' => 'Fecha de pago inválida.',
            'payment_date.before_or_equal' => 'La fecha de pago no puede ser futura.',
            'notes.required' => 'Las notas del pago son obligatorias para esta empresa.',
            'idempotency_key.uuid' => 'La clave de idempotencia debe ser un UUID válido.',
            'confirm_duplicate.boolean' => 'La confirmación de otro cobro debe ser sí o no.',
            'offline_created_at.before_or_equal' => 'La fecha offline no puede ser futura.',
            'offline_created_at.after' => 'La fecha offline no puede tener más de 7 días de antigüedad.',
        ];
    }

    /**
     * Hace el campo 'notes' obligatorio cuando la empresa lo requiere.
     */
    public function withValidator(Validator $validator): void
    {
        $settings = CompanyFinancialSettings::forCompany($this->user()->company_id);

        if ($settings->require_payment_notes) {
            $validator->addRules(['notes' => ['required', 'string', 'max:500']]);
        }
    }

    /**
     * Prepara los datos para validación.
     * - Auto-genera idempotency_key (UUID) cuando el cliente no la envía.
     *   Garantiza que TODOS los pagos tengan una clave de idempotencia, eliminando
     *   el riesgo de duplicados en reintentos de red sin clave explícita.
     * - Normaliza la fecha de pago a formato Y-m-d.
     * - Convierte monto simplificado a valor real si aplica.
     */
    protected function prepareForValidation(): void
    {
        if (empty($this->idempotency_key)) {
            $this->merge(['idempotency_key' => (string) Str::uuid()]);
        }

        if ($this->has('payment_date') && $this->payment_date) {
            try {
                // Parsear la fecha y extraer solo la parte de fecha (sin hora)
                // Esto maneja casos donde el cliente envía datetime con timezone
                $parsed = Carbon::parse($this->payment_date);
                $this->merge([
                    'payment_date' => $parsed->toDateString(),
                ]);
            } catch (\Exception $e) {
                // Si falla el parse, dejar el valor original para que falle la validación
            }
        }

        // Handle simplified input conversion
        if ($this->boolean('is_simplified_amount') && $this->amount) {
            $settings = CompanyFinancialSettings::forCompany($this->user()->company_id);

            if ($settings->isSimplifiedInputEnabled('pwa_payment_create')) {
                $realAmount = $settings->convertSimplifiedToReal((float) $this->amount);
                $this->merge([
                    'amount' => $realAmount,
                    'original_simplified_amount' => $this->amount,
                ]);
            }
        }
    }
}
