<?php

declare(strict_types=1);

namespace Tests\Unit;

use Carbon\Carbon;
use PHPUnit\Framework\TestCase;

/**
 * Tests para validación de fecha de pago.
 *
 * Test unitario puro que verifica la lógica de comparación de fechas
 * usada en PaymentValidationService.
 */
class PaymentDateValidationTest extends TestCase
{
    /*=========================================================
    | PAGOS RETROACTIVOS (FECHAS PASADAS)
    =========================================================*/

    public function test_payment_with_past_date_is_valid(): void
    {
        $pastDate = Carbon::today()->subDays(7);
        $today = Carbon::today();

        // La lógica en PaymentValidationService:
        // $paymentDateObj->gt($todayDate) debe ser false para fechas válidas
        $isValid = ! $pastDate->startOfDay()->gt($today);

        $this->assertTrue($isValid, 'Pago con fecha pasada debería ser válido');
    }

    public function test_payment_with_yesterday_date_is_valid(): void
    {
        $yesterday = Carbon::yesterday();
        $today = Carbon::today();

        $isValid = ! $yesterday->startOfDay()->gt($today);

        $this->assertTrue($isValid, 'Pago con fecha de ayer debería ser válido');
    }

    /*=========================================================
    | PAGOS DE HOY
    =========================================================*/

    public function test_payment_with_today_date_is_valid(): void
    {
        $paymentDate = Carbon::today();
        $today = Carbon::today();

        $isValid = ! $paymentDate->startOfDay()->gt($today);

        $this->assertTrue($isValid, 'Pago con fecha de hoy debería ser válido');
    }

    /**
     * Edge case: Pago registrado a las 11:59pm sigue siendo válido.
     */
    public function test_payment_at_end_of_day_is_valid(): void
    {
        // Simular que estamos a las 11:59pm
        Carbon::setTestNow(Carbon::today()->setTime(23, 59, 59));

        $paymentDate = Carbon::parse(Carbon::now()->toDateString()); // Solo fecha
        $today = Carbon::today();

        $isValid = ! $paymentDate->startOfDay()->gt($today);

        $this->assertTrue($isValid, 'Pago a las 11:59pm debería ser válido');

        Carbon::setTestNow(); // Reset
    }

    /*=========================================================
    | FECHAS FUTURAS (DEBEN SER RECHAZADAS)
    =========================================================*/

    public function test_payment_with_tomorrow_date_is_rejected(): void
    {
        $tomorrow = Carbon::tomorrow();
        $today = Carbon::today();

        $isValid = ! $tomorrow->startOfDay()->gt($today);

        $this->assertFalse($isValid, 'Pago con fecha de mañana debería ser rechazado');
    }

    public function test_payment_with_future_date_is_rejected(): void
    {
        $futureDate = Carbon::today()->addDays(7);
        $today = Carbon::today();

        $isValid = ! $futureDate->startOfDay()->gt($today);

        $this->assertFalse($isValid, 'Pago con fecha futura debería ser rechazado');
    }

    /*=========================================================
    | EDGE CASES DE TIMEZONE
    =========================================================*/

    /**
     * Simula el bug original: cliente envía datetime con timezone offset.
     */
    public function test_payment_with_datetime_and_timezone_offset(): void
    {
        // Cliente en Colombia (UTC-5) envía: 2024-01-15T22:00:00-05:00
        // Cuando se parsea, debería seguir siendo "15 de enero" en timezone local

        $dateWithTimezone = '2024-01-15T22:00:00-05:00';
        $parsed = Carbon::parse($dateWithTimezone);

        // Extraer solo la fecha (sin hora) - esto es lo que hace prepareForValidation
        $normalizedDate = $parsed->toDateString();

        // La fecha debería ser 15 de enero (en timezone -05:00)
        $this->assertEquals('2024-01-15', $normalizedDate);
    }

    /**
     * Test del bug específico: El isFuture() comparaba con now() incluyendo hora.
     * Esto causaba que un pago de "hoy" (fecha) pudiera ser considerado "futuro"
     * si la hora del pago era 00:00:00 pero now() era, digamos, 10pm del día anterior en UTC.
     */
    public function test_date_only_comparison_avoids_hour_issues(): void
    {
        // Supongamos que "ahora" son las 11:30pm del 15 de enero
        Carbon::setTestNow(Carbon::parse('2024-01-15 23:30:00'));

        // El cliente envía payment_date = '2024-01-15' (sin hora)
        $paymentDate = Carbon::parse('2024-01-15'); // Esto se interpreta como 00:00:00

        // Con la VIEJA lógica (isFuture):
        // isFuture compara: 2024-01-15 00:00:00 > 2024-01-15 23:30:00 = false, OK
        $oldValidation = ! $paymentDate->isFuture();

        // Pero si el servidor estuviera en UTC y el cliente en UTC-5:
        // now() = 2024-01-16 04:30:00 UTC
        // payment = 2024-01-15 00:00:00 (local, que es 2024-01-15 05:00:00 UTC)
        // isFuture: 2024-01-15 05:00:00 > 2024-01-16 04:30:00 = false, también OK

        // El problema real es cuando:
        // Cliente en UTC-5 a las 8pm del 15 = UTC 1am del 16
        // now() en servidor UTC = 2024-01-16 01:00:00
        // Cliente quiere registrar pago del "15 de enero" (su fecha local)
        // Si envía '2024-01-15' y el servidor lo compara con today() en UTC...
        // today() en UTC = 2024-01-16
        // Validación fallará porque 15 < 16

        // NUEVA lógica usa startOfDay() para ambos:
        $paymentDateObj = Carbon::parse('2024-01-15')->startOfDay();
        $todayDate = Carbon::today(); // Midnight de hoy en timezone de la app

        $newValidation = ! $paymentDateObj->gt($todayDate);

        $this->assertTrue($newValidation, 'Nueva lógica permite pago de hoy');

        Carbon::setTestNow(); // Reset
    }

    /**
     * Verifica que diferentes formatos de fecha se normalizan correctamente.
     */
    public function test_payment_date_formats_normalize_to_date_only(): void
    {
        $formats = [
            '2024-01-15' => '2024-01-15',
            '2024-01-15T10:30:00' => '2024-01-15',
            '2024-01-15T10:30:00Z' => '2024-01-15',
            '2024-01-15T10:30:00-05:00' => '2024-01-15',
        ];

        foreach ($formats as $input => $expected) {
            $parsed = Carbon::parse($input);
            $normalized = $parsed->toDateString();

            $this->assertEquals(
                $expected,
                $normalized,
                "Formato '{$input}' debería normalizarse a '{$expected}'"
            );
        }
    }
}
