<?php

/**
 * The public-facing functionality of the plugin.
 * Handles Limit Login Attempts logic.
 *
 * @package Wporlogin
 * @subpackage Wporlogin/public
 */
class Wporlogin_Public_Limit_Login {

    private $plugin_name;
    private $version;

    public function __construct( $plugin_name, $version ) {
        $this->plugin_name = $plugin_name;
        $this->version     = $version;
    }

    /**
     * Check if the user is locked out before authentication.
     * Hook: wp_authenticate_user
     */
    public function check_lockout( $user, $password ) {

        // LOG 1: Ver si entra a la función
        error_log( 'WPORLOGIN DEBUG: Comprobando bloqueo para usuario...' );

        if ( get_option( 'wporlogin_limit_enable' ) != '1' ) {
            return $user;
        }

        $ip = $this->get_client_ip();
        $transient_name = 'wporlogin_lockout_' . md5( $ip );

        if ( get_transient( $transient_name ) ) {
            $default_msg = __( 'You have exceeded the maximum number of login attempts. Please try again later.', 'wporlogin' );
            $custom_msg  = get_option( 'wporlogin_limit_message', $default_msg );
            
            // Si el mensaje está vacío, usar default
            if ( empty( $custom_msg ) ) {
                $custom_msg = $default_msg;
            }

            return new WP_Error( 'wporlogin_locked', $custom_msg );
        }

        return $user;
    }

   /**
     * Cuenta los fallos y BLOQUEA guardando la fecha exacta.
     */
    public function handle_failed_login( $username ) {
        if ( get_option( 'wporlogin_limit_enable' ) != '1' ) {
            return;
        }

        $ip = $this->get_client_ip();
        $transient_count = 'wporlogin_retries_' . md5( $ip );
        $transient_lock  = 'wporlogin_lockout_' . md5( $ip );

        $max_retries = (int) get_option( 'wporlogin_limit_max_retries', 3 );
        $lock_time   = (int) get_option( 'wporlogin_limit_lock_time', 20 );

        $attempts = (int) get_transient( $transient_count );

        if ( ! $attempts ) {
            set_transient( $transient_count, 1, HOUR_IN_SECONDS );
            $attempts = 1;
        } else {
            $attempts++;
            set_transient( $transient_count, $attempts, HOUR_IN_SECONDS );
        }

        if ( $attempts >= $max_retries ) {
            // --- CAMBIO AQUÍ ---
            // Guardamos un Array con la IP y la Hora actual (Timestamp)
            $block_data = array(
                'ip' => $ip,
                'start_time' => time() // Hora exacta del bloqueo
            );

            set_transient( $transient_lock, $block_data, $lock_time * MINUTE_IN_SECONDS );
            delete_transient( $transient_count );
        }
    }

    /**
     * Genera el reporte recuperando la hora y formateándola.
     */
    public static function get_blocked_ips_report() {
        global $wpdb;
        $sql = "SELECT option_value, option_name FROM $wpdb->options WHERE option_name LIKE '_transient_wporlogin_lockout_%'";
        $results = $wpdb->get_results( $sql );

        $blocked = [];
        foreach ( $results as $row ) {
            // Recuperamos los datos. Puede ser un string (versión vieja) o un array (versión nueva)
            $data = maybe_unserialize( $row->option_value );
            
            // Normalizamos los datos
            $ip_address = '';
            $start_date = __( 'Unknown', 'wporlogin' );

            if ( is_array( $data ) && isset( $data['ip'] ) ) {
                // Es la nueva versión con fecha
                $ip_address = $data['ip'];
                if ( isset( $data['start_time'] ) ) {
                    // Formateamos la fecha al formato local de WordPress
                    $start_date = date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $data['start_time'] );
                }
            } else {
                // Es la versión antigua (solo string IP) o localhost
                $ip_address = $data; // En versiones anteriores guardábamos solo la IP
            }

            // Calculamos tiempo restante
            $timeout_option = '_transient_timeout_' . substr( $row->option_name, 11 );
            $timeout_val = get_option( $timeout_option );
            
            $time_left = __( 'Unknown', 'wporlogin' );
            if ( $timeout_val ) {
                $seconds = $timeout_val - time();
                if ($seconds > 0) {
                    $time_left = ceil( $seconds / 60 ) . ' ' . __( 'min', 'wporlogin' );
                }
            }

            $blocked[] = [
                'ip'         => $ip_address,
                'start_date' => $start_date,
                'time_left'  => $time_left
            ];
        }
        return $blocked;
    }

    /**
     * Get client IP address.
     */
    private function get_client_ip() {
        $ip = $_SERVER['REMOTE_ADDR'];
        if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
            $ip = $_SERVER['HTTP_CLIENT_IP'];
        } elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
            $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
        }
        return substr( preg_replace( '/[^0-9a-fA-F:., ]/', '', $ip ), 0, 50 );
    }

    /**
     * Muestra el número exacto de intentos restantes.
     * CORRECCIÓN: Ahora verifica primero si ya está bloqueado para no mostrar mensajes erróneos.
     * Hook: login_errors
     */
    public function show_remaining_attempts( $error ) {
        // 1. Validar si la función está activa
        if ( get_option( 'wporlogin_limit_enable' ) != '1' ) {
            return $error;
        }

        // 2. Si el error no es texto, salir
        if ( ! is_string( $error ) ) {
            return $error;
        }

        $ip = $this->get_client_ip();
        
        // --- CORRECCIÓN DE SINCRONIZACIÓN ---
        // Verificamos si existe el bloqueo (Transient de Lockout)
        $transient_lock = 'wporlogin_lockout_' . md5( $ip );
        
        if ( get_transient( $transient_lock ) ) {
            // Si YA está bloqueado, no mostramos "te queda 1 intento".
            // Devolvemos el error tal cual (que será el mensaje de bloqueo).
            return $error;
        }
        // ------------------------------------
        
        // 3. Si no está bloqueado, calculamos matemáticas
        $transient_count = 'wporlogin_retries_' . md5( $ip );
        $attempts = (int) get_transient( $transient_count );
        $max_retries = (int) get_option( 'wporlogin_limit_max_retries', 3 );

        // Si attempts es 0 (porque se borró o es el primer intento), remaining es igual al máximo.
        // No queremos avisar "Quedan 3 intentos" en el primer fallo.
        if ( $attempts <= 0 ) {
            return $error;
        }

        $remaining = $max_retries - $attempts;

        // 4. FILTRO DE UX: Avisar solo cuando quede 1 intento (Última oportunidad)
        if ( $remaining !== 1 ) {
            return $error;
        }

        // 5. Mensaje
        $warning = sprintf( 
            __( ' <strong>Warning</strong>: Only %d attempt remaining.', 'wporlogin' ), 
            $remaining 
        );

        return $error . ' <br/>' . $warning;
    }

    /**
     * Resetea el contador de fallos cuando el usuario inicia sesión correctamente.
     * Este hook asegura que los intentos fallidos anteriores se olviden.
     * Hook: wp_login
     */
    public function reset_counter_on_success( $user_login, $user ) {
        $ip = $this->get_client_ip();
        $transient_name = 'wporlogin_retries_' . md5( $ip );
        
        // El reset es simple: borramos el Transient de conteo.
        delete_transient( $transient_name );
        
        // Nota: No es necesario borrar el Transient de bloqueo (lockout) aquí, 
        // porque si el usuario tuvo éxito, no estaba bloqueado. Pero por si acaso 
        // queremos ser puristas, podríamos borrarlo:
        // $lockout_name = 'wporlogin_lockout_' . md5( $ip );
        // delete_transient( $lockout_name );
    }
}