<?php

/**
 * The public-facing functionality of the plugin.
 *
 * @link       https://wpgeenie.org
 * @since      1.0.0
 *
 * @package    Spending_Limits_For_Woocommerce
 * @subpackage Spending_Limits_For_Woocommerce/public
 */
/**
 * The public-facing functionality of the plugin.
 *
 * Defines the plugin name, version, and two examples hooks for how to
 * enqueue the public-facing stylesheet and JavaScript.
 *
 * @package    Spending_Limits_For_Woocommerce
 * @subpackage Spending_Limits_For_Woocommerce/public
 * @author     wpgeenie.org <info@wpgeenie.org>
 */
class Spending_Limits_For_Woocommerce_Public {
    /**
     * The ID of this plugin.
     *
     * @since    1.0.0
     * @access   private
     * @var      string    $plugin_name    The ID of this plugin.
     */
    private $plugin_name;

    /**
     * The version of this plugin.
     *
     * @since    1.0.0
     * @access   private
     * @var      string    $version    The current version of this plugin.
     */
    private $version;

    /**
     * Holds the options array name stored in wp_options.
     *
     * @var string
     */
    private static $option_group = 'slfw_options';

    /**
     * Initialize the class and set its properties.
     *
     * @since    1.0.0
     * @param string $plugin_name       The name of the plugin.
     * @param string $version    The version of this plugin.
     */
    public function __construct( $plugin_name, $version ) {
        $this->plugin_name = $plugin_name;
        $this->version = $version;
    }

    /**
     * Helper function to retrieve a single option value.
     * This is useful for retrieving data throughout your plugin.
     *
     * @param string $key The option key.
     * @param mixed  $def Optional default value if not set.
     * @return mixed The option value.
     */
    private static function get_option( $key, $def = false ) {
        $options = get_option( self::$option_group, array() );
        return ( isset( $options[$key] ) ? $options[$key] : $def );
    }

    /**
     * Get user's spending limit data as a structured array.
     *
     * @param  int $user_id user ID to retrieve data for.
     * @return array{amount: float, period: string, custom_period: int, custom_period_time: string}
     */
    private static function get_user_spend_limit_data( $user_id ) {
        return array(
            'amount'             => floatval( get_user_meta( $user_id, 'spend_limit_amount', true ) ),
            'period'             => get_user_meta( $user_id, 'spend_limit_period', true ),
            'custom_period'      => intval( ( get_user_meta( $user_id, 'spend_limit_period_custom', true ) ? intval( get_user_meta( $user_id, 'spend_limit_period_custom', true ) ) : 0 ) ),
            'custom_period_time' => ( get_user_meta( $user_id, 'spend_limit_period_custom_time', true ) ? get_user_meta( $user_id, 'spend_limit_period_custom_time', true ) : false ),
        );
    }

    /**
     * Check if the user's account is currently locked based on the 'spending_limit_lock_until' meta value.
     *
     * @param  int $user_id User ID to check.
     * @return bool
     */
    private static function is_account_locked( $user_id ) {
        $lock_until = get_user_meta( $user_id, 'spending_limit_lock_until', true );
        return $lock_until && ('permanent' === $lock_until || strtotime( $lock_until ) > time());
    }

    /**
     * Get the lock until date for a user, or false if not set.
     *
     * @param  int $user_id User ID to retrieve lock date for.
     * @return string|false
     */
    private static function get_lock_until( $user_id ) {
        return ( get_user_meta( $user_id, 'spending_limit_lock_until', true ) ? get_user_meta( $user_id, 'spending_limit_lock_until', true ) : false );
    }

    /**
     * Check if admin limit should be applied for the user based on plugin settings and user meta.
     *
     * @param  int $user_id User ID to check.
     * @return bool
     */
    private static function should_apply_admin_limit( $user_id ) {
        if ( '1' !== self::get_option( 'enable_limit_for_all' ) ) {
            return false;
        }
        $override = '1' === self::get_option( 'enable_limit_for_all_override' );
        $limit_override = '1' === self::get_option( 'enable_limit_for_all_limit_override' );
        $has_user_limit = (bool) get_user_meta( $user_id, 'spend_limit_amount', true );
        if ( !$override || !$has_user_limit ) {
            return true;
        }
        if ( $override && !$limit_override ) {
            return true;
        }
        return false;
    }

    /**
     * Format the period label for display, handling custom periods if necessary.
     *
     * @param  string $period The period type (e.g. 'day', 'week', 'month', 'year', or 'custom').
     * @param  int    $custom_period The custom period value (e.g. number of days/weeks/months/years).
     * @param  string $custom_period_time The custom period time unit (e.g. 'days', 'weeks', 'months', 'years').
     * @return string
     */
    private static function format_period_label( $period, $custom_period = 0, $custom_period_time = '' ) {
        if ( 'custom' !== $period ) {
            return $period;
        }
        /* translators: 1: broj, 2: vremenski period */
        return sprintf( 
            /* translators: 1: custom period value, 2: custom period time unit */
            _n(
                '%1$s %2$s',
                '%1$s %2$s',
                $custom_period,
                'spending-limits-for-woocommerce'
            ),
            $custom_period,
            $custom_period_time
         );
    }

    /**
     * Remove checkout buttons if user is over limit or account is locked.
     *
     * @return void
     */
    private function remove_checkout_buttons() {
        remove_action( 'woocommerce_proceed_to_checkout', 'wc_get_pay_buttons', 10 );
        remove_action( 'woocommerce_proceed_to_checkout', 'woocommerce_button_proceed_to_checkout', 20 );
    }

    /**
     * Get the current cart total, or 0 if the cart is not available.
     *
     * @return float
     */
    private static function get_cart_total() {
        return ( WC()->cart ? floatval( WC()->cart->total ) : 0.0 );
    }

    /**
     * Convert a period string to a number of days, handling custom periods if necessary.
     *
     * @param string $period The period type (e.g. 'day', 'week', 'month', 'year', or 'custom').
     * @param int    $custom_period The custom period value (e.g. number of days/weeks/months/years).
     * @param string $custom_period_time The custom period time unit (e.g. 'days', 'weeks', 'months', 'years').
     * @return int Number of days corresponding to the given period.
     */
    private function period_to_days( $period, $custom_period = 0, $custom_period_time = 'weeks' ) {
        $map = array(
            'day'   => 1,
            'week'  => 7,
            'month' => 30,
            'year'  => 365,
        );
        if ( 'custom' === $period ) {
            return ( 'weeks' === $custom_period_time ? $custom_period * 7 : intval( $custom_period ) );
        }
        return $map[$period] ?? 1;
    }

    /**
     * Calculate the daily amount from an already-fetched limit data array.
     *
     * @param float  $amount The total amount of the limit.
     * @param string $period The period type (e.g. 'day', 'week', 'month', 'year', or 'custom').
     * @param array  $limit The limit data array containing 'custom_period' and 'custom_period_time' if period is 'custom'.
     * @param array  $period_days An array mapping standard period types to their corresponding number of days.
     * @return int The calculated daily amount based on the limit data and period.
     */
    private static function calculate_per_day_from_meta(
        $amount,
        $period,
        array $limit,
        $period_days
    ) {
        if ( 'custom' === $period ) {
            $cp = $limit['custom_period'];
            $ct = ( $limit['custom_period_time'] ? $limit['custom_period_time'] : 'weeks' );
            $days = ( 'weeks' === $ct ? $cp * 7 : $cp );
        } else {
            $days = $period_days[$period] ?? 1;
        }
        return ( $days > 0 ? intval( $amount ) / $days : 0 );
    }

    /**
     * Calculate the daily amount for the newly submitted limit (from POST data).
     *
     * @param float  $amount The total amount of the new limit.
     * @param string $period The period type (e.g. 'day', 'week', 'month', 'year', or 'custom').
     * @param int    $custom_period The custom period value (e.g. number of days/weeks/months/years) from POST data.
     * @param string $custom_time The custom period time unit (e.g. 'days', 'weeks', 'months', 'years') from POST data.
     * @param array  $period_days An array mapping standard period types to their corresponding number of days.
     * @return int The calculated daily amount based on the submitted limit data and period.
     */
    private static function calculate_per_day_for_new(
        $amount,
        $period,
        $custom_period,
        $custom_time,
        $period_days
    ) {
        if ( 'custom' === $period ) {
            $days = ( 'weeks' === $custom_time ? intval( $custom_period ) * 7 : intval( $custom_period ) );
        } else {
            $days = $period_days[$period] ?? 1;
        }
        return ( $days > 0 ? intval( $amount ) / $days : 0 );
    }

    /**
     * Handle a checkout error — throw a Store API exception or add a WC notice.
     *
     * @param string $code The error code for the exception (if in Store API context).
     * @param string $message The error message to display or include in the exception.
     * @throws \Automattic\WooCommerce\StoreApi\Exceptions\RouteException When in Store API context and a checkout error occurs.
     * @return void
     */
    private function handle_checkout_error( $code, $message ) {
        if ( doing_action( 'woocommerce_store_api_checkout_update_order_from_request' ) ) {
            throw new \Automattic\WooCommerce\StoreApi\Exceptions\RouteException(esc_attr( $code ), wp_kses_post( $message ), 400);
        }
        if ( !wc_has_notice( $message, 'error' ) ) {
            wc_add_notice( $message, 'error' );
        }
        remove_action( 'woocommerce_checkout_order_processed', 'woocommerce_order_data_store_cpt::create', 10 );
    }

    /**
     * Register the stylesheets for the public-facing side of the site.
     *
     * @since    1.0.0
     */
    public function enqueue_styles() {
        if ( !is_account_page() ) {
            wp_enqueue_style(
                $this->plugin_name,
                plugin_dir_url( __FILE__ ) . 'css/spending-limits-for-woocommerce-public.css',
                array(),
                $this->version,
                'all'
            );
        }
    }

    /**
     * Register the JavaScript for the public-facing side of the site.
     *
     * @since    1.0.0
     */
    public function enqueue_scripts() {
        if ( is_account_page() ) {
            wp_enqueue_script(
                $this->plugin_name,
                plugin_dir_url( __FILE__ ) . 'js/spending-limits-for-woocommerce-public.js',
                array('jquery'),
                $this->version,
                false
            );
        }
    }

    /**
     * Look in this plugin for WooCommerce template overrides.
     *
     * For example, if you want to override woocommerce/templates/cart/cart.php, you
     * can place the modified template in <plugindir>/custom/templates/woocommerce/cart/cart.php
     *
     * @param string $located is the currently located template, if any was found so far.
     * @param string $template_name is the name of the template (ex: cart/cart.php).
     * @param string $args array().
     * @param string $template_path is the name of the template (ex: cart/cart.php).
     * @param string $default_path is the name of the template (ex: cart/cart.php).
     * @return string $located is the newly located template if one was found, otherwise
     *                         it is the previously found template.
     */
    public static function wc_get_template(
        $located,
        $template_name,
        $args,
        $template_path,
        $default_path
    ) {
        if ( empty( $located ) || !file_exists( $located ) ) {
            $plugin_template_path = untrailingslashit( plugin_dir_path( __DIR__ ) ) . '/templates/woocommerce/' . $template_name;
            if ( file_exists( $plugin_template_path ) ) {
                $located = $plugin_template_path;
            }
        }
        return $located;
    }

    /**
     * Add my account tab
     *
     * @param  array $items - all tabs items.
     * @return array $items
     */
    public static function add_my_account_tab( $items ) {
        $spending_limits = array(
            'spending-limits' => esc_html__( 'Spending Limits', 'spending-limits-for-woocommerce' ),
        );
        $after_key = 'payment-methods';
        $index = array_search( $after_key, array_keys( $items ), true );
        $items = array_slice( $items, 0, $index + 1 ) + $spending_limits + $items;
        return $items;
    }

    /**
     * Register the endpoint for the spending_limits tab
     *
     * @return void
     */
    public static function add_my_account_endpoint() {
        add_rewrite_endpoint( 'spending-limits', EP_ROOT | EP_PAGES );
    }

    /**
     * Get template for endpoint
     **/
    public static function my_account_content() {
        wc_get_template( 'myaccount/spending-limits.php' );
    }

    /**
     * Process form submission
     *
     * @return void
     */
    public static function process_form_submission() {
        self::process_spend_limit_form();
        self::process_lock_form();
    }

    /**
     * Process spend limit form submission
     *
     * @return void
     */
    private static function process_spend_limit_form() {
        if ( !isset( 
            $_POST['spend_limit_submit'],
            $_POST['spend_limit_amount'],
            $_POST['spend_limit_period'],
            $_POST['spend_limit_submit_nonce']
         ) ) {
            return;
        }
        if ( !wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['spend_limit_submit_nonce'] ) ), 'spend_limit_submit' ) ) {
            return;
        }
        $user_id = get_current_user_id();
        $options = get_option( 'slfw_options' );
        $period_days = array(
            'day'   => 1,
            'week'  => 7,
            'month' => 30,
            'year'  => 365,
        );
        $new_amount = floatval( $_POST['spend_limit_amount'] );
        $new_period = sanitize_text_field( wp_unslash( $_POST['spend_limit_period'] ) );
        $custom_period = ( isset( $_POST['spend_limit_period_custom'] ) ? abs( intval( sanitize_text_field( wp_unslash( $_POST['spend_limit_period_custom'] ) ) ) ) : false );
        $custom_time = ( isset( $_POST['spend_limit_period_custom_time'] ) ? sanitize_text_field( wp_unslash( $_POST['spend_limit_period_custom_time'] ) ) : false );
        $current = self::get_user_spend_limit_data( $user_id );
        $current_amount = $current['amount'];
        $current_period = $current['period'];
        // --- Admin limit check ---
        $admin_limit_for_all = isset( $options['enable_limit_for_all'] ) && '1' === $options['enable_limit_for_all'];
        $admin_override = isset( $options['enable_limit_for_all_override'] ) && '1' === $options['enable_limit_for_all_override'];
        $admin_limit_override = isset( $options['enable_limit_for_all_limit_override'] ) && '1' === $options['enable_limit_for_all_limit_override'];
        $admin_amount = $options['enable_limit_for_all_value'] ?? 0;
        $admin_period = $options['enable_limit_for_all_period'] ?? 'day';
        if ( $admin_limit_for_all && $admin_amount && $admin_period && $admin_override && !$admin_limit_override ) {
            $admin_per_day = intval( $admin_amount ) / ($period_days[$admin_period] ?? 1);
            $new_per_day = intval( $new_amount ) / ($period_days[$new_period] ?? 1);
            if ( $new_per_day > $admin_per_day ) {
                /* translators: 1: amount, 2: period */
                wc_add_notice( sprintf( esc_html__( 'Your spending limit must be less than the admin limit. Current set admin limit is set to %1$s per %2$s', 'spending-limits-for-woocommerce' ), wc_price( $admin_amount ), $admin_period ), 'error' );
                return;
            }
        }
        // --- Can the user increase their limit? ---
        $can_increase = true;
        if ( $current_amount && $current_period ) {
            $current_per_day = self::calculate_per_day_from_meta(
                $current_amount,
                $current_period,
                $current,
                $period_days
            );
            $new_per_day = self::calculate_per_day_for_new(
                $new_amount,
                $new_period,
                $custom_period,
                $custom_time,
                $period_days
            );
            if ( $new_per_day >= $current_per_day ) {
                $can_increase = false;
            }
            // --- Cooling-off period check ---
            $log_time = self::get_last_limit_date_from_logs( $user_id );
            if ( $log_time ) {
                $current_period_days = $period_days[$current_period] ?? 1;
                $log_timestamp = strtotime( $log_time );
                $time_diff = current_time( 'timestamp' ) - $log_timestamp;
                $next_change_timestamp = $log_timestamp + $current_period_days * DAY_IN_SECONDS;
                $next_change_string = date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $next_change_timestamp );
                if ( $time_diff < $current_period_days * DAY_IN_SECONDS ) {
                    /* translators: 1: period, 2: date */
                    wc_add_notice( sprintf( esc_html__( 'You cannot modify your spending limit. You can do this only once in a %1$s. Next allowed change: %2$s', 'spending-limits-for-woocommerce' ), $new_period, $next_change_string ), 'error' );
                    return;
                }
            }
        }
        if ( $can_increase ) {
            self::save_spend_limit(
                $user_id,
                $new_amount,
                $new_period,
                $custom_period,
                $custom_time
            );
        } else {
            wc_add_notice( esc_html__( 'You cannot increase your spending limit.', 'spending-limits-for-woocommerce' ), 'error' );
        }
    }

    /**
     * Save the spend limit data for the user and log the change.
     *
     * @param  mixed $user_id The user ID.
     * @param  mixed $amount The amount of the new limit.
     * @param  mixed $period The period type (e.g. 'day', 'week', 'month', 'year', or 'custom').
     * @param  mixed $custom_period The custom period value (e.g. number of days/weeks/months/years).
     * @param  mixed $custom_time The custom period time unit (e.g. 'days', 'weeks', 'months', 'years').
     * @return void
     */
    private static function save_spend_limit(
        $user_id,
        $amount,
        $period,
        $custom_period,
        $custom_time
    ) {
        update_user_meta( $user_id, 'spend_limit_amount', $amount );
        update_user_meta( $user_id, 'spend_limit_period', $period );
        $period_label = ( 'custom' === $period ? sprintf( _n(
            '%1$s %2$s',
            '%1$s %2$s',
            $custom_period,
            'spending-limits-for-woocommerce'
        ), $custom_period, $custom_time ) : $period );
        /* translators: 1: amount, 2: period */
        $message = sprintf( esc_html__( 'Limit set %1$s per %2$s.', 'spending-limits-for-woocommerce' ), wp_strip_all_tags( wc_price( $amount ) ), $period_label );
        Spending_Limits_For_Woocommerce::insert_log(
            $user_id,
            $message,
            6,
            get_current_user_id()
        );
        do_action(
            'spending_limits_for_woocommerce_add_limit',
            $user_id,
            $amount,
            $period_label
        );
    }

    /**
     * Process lock form submission
     *
     * @return void
     */
    private static function process_lock_form() {
        if ( !isset( $_POST['lock_length_submit'], $_POST['locklength'], $_POST['lock_length_submit_nonce'] ) ) {
            return;
        }
        if ( !wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['lock_length_submit_nonce'] ) ), 'lock_length_submit' ) ) {
            return;
        }
        $user_id = get_current_user_id();
        if ( get_user_meta( $user_id, 'spending_limit_lock_until', true ) ) {
            return;
        }
        $lock_map = array(
            '1w'        => '+1 week',
            '4w'        => '+4 weeks',
            '12w'       => '+12 weeks',
            '1y'        => '+1 year',
            'permanent' => 'permanent',
        );
        $locklength = sanitize_text_field( wp_unslash( $_POST['locklength'] ) );
        if ( !isset( $lock_map[$locklength] ) ) {
            return;
        }
        $lock_until = ( 'permanent' === $lock_map[$locklength] ? 'permanent' : gmdate( 'Y-m-d H:i:s', strtotime( $lock_map[$locklength] ) ) );
        /* translators: date */
        Spending_Limits_For_Woocommerce::insert_log(
            $user_id,
            sprintf( esc_html__( 'Lock until %s. ', 'spending-limits-for-woocommerce' ), esc_html( $lock_until ) ),
            5,
            get_current_user_id()
        );
        update_user_meta( $user_id, 'spending_limit_lock_until', $lock_until );
        do_action( 'spending_limits_for_woocommerce_add_lock', $user_id, $lock_until );
    }

    /**
     * Show_spending_limit_notice
     *
     * @return void
     */
    public function show_spending_limit_notice() {
        if ( !(is_checkout() || is_cart() || !is_user_logged_in()) ) {
            return;
        }
        $user_id = get_current_user_id();
        $user_id = get_current_user_id();
        if ( slfw_fs()->is__premium_only() && $this->should_apply_admin_limit( $user_id ) ) {
            $message = ( $this->is_user_over_admin_limit( $user_id ) ? $this->get_option( 'enable_limit_for_all_message_when_limit_reached' ) : $this->get_option( 'enable_limit_for_all_message' ) );
            if ( !wc_has_notice( $message, 'notice' ) ) {
                wc_add_notice( $message, 'notice' );
            }
            return;
        }
        $limit = $this->get_user_spend_limit_data( $user_id );
        if ( !$limit['amount'] || !$limit['period'] ) {
            return;
        }
        $period_label = $this->format_period_label( $limit['period'], $limit['custom_period'], $limit['custom_period_time'] );
        /* translators: 1: amount, 2: period */
        $message = sprintf( esc_html__( 'Your spending limit is set to %1$s per %2$s.', 'spending-limits-for-woocommerce' ), wc_price( $limit['amount'] ), esc_html( $period_label ) );
        if ( !wc_has_notice( $message, 'notice' ) ) {
            wc_add_notice( $message, 'notice' );
        }
    }

    /** Check if user is over soending limit
     *
     * @return void
     * @throws \Automattic\WooCommerce\StoreApi\Exceptions\RouteException When user is over spending limit.
     */
    public function prevent_checkout_if_over_limit() {
        if ( !is_user_logged_in() || self::get_cart_total() === floatval( 0 ) ) {
            return;
        }
        $user_id = get_current_user_id();
        $cart_total = $this->get_cart_total();
        if ( $this->should_apply_admin_limit( $user_id ) && $this->is_user_over_admin_limit( $user_id, $cart_total ) ) {
            $message = sprintf( 
                /* translators: 1.value 2.period */
                esc_html__( 'You have reached your spending limit of %1$s per %2$s set by admin, or your order exceeds it.', 'spending-limits-for-woocommerce' ),
                html_entity_decode( wp_strip_all_tags( wc_price( $this->get_option( 'enable_limit_for_all_value' ) ) ) ),
                esc_html( $this->get_option( 'enable_limit_for_all_period' ) )
             );
            $this->handle_checkout_error( 'over_admin_spending_limit', $message );
            return;
        }
        $limit = $this->get_user_spend_limit_data( $user_id );
        if ( $limit['amount'] && $limit['period'] && $this->is_user_over_limit( $user_id, $cart_total ) ) {
            $period_label = $this->format_period_label( $limit['period'], $limit['custom_period'], $limit['custom_period_time'] );
            $message = sprintf( 
                /* translators: 1.value 2.period */
                esc_html__( 'You have reached your spending limit of %1$s per %2$s, or your order exceeds it.', 'spending-limits-for-woocommerce' ),
                html_entity_decode( wp_strip_all_tags( wc_price( $limit['amount'] ) ) ),
                esc_html( $period_label )
             );
            $this->handle_checkout_error( 'over_spending_limit', $message );
        }
    }

    /** Remove gateways if user is over spending limit
     *
     * @param  array $gateways available gateways.
     * @return array of available gateways
     */
    public function remove_gateways_if_over_limit( $gateways ) {
        if ( !is_user_logged_in() ) {
            return $gateways;
        }
        $user_id = get_current_user_id();
        $cart_total = $this->get_cart_total();
        if ( $this->should_apply_admin_limit( $user_id ) && $this->is_user_over_admin_limit( $user_id, $cart_total ) ) {
            return array();
        }
        $limit = $this->get_user_spend_limit_data( $user_id );
        if ( $limit['amount'] && $limit['period'] && $this->is_user_over_limit( $user_id, $cart_total ) ) {
            return array();
        }
        return $gateways;
    }

    /**
     * Prevent_add_to_cart_if_locked
     *
     * @param  mixed $cart_item_key cart key.
     * @param  mixed $product_id product id.
     * @return void
     */
    public static function prevent_add_to_cart_if_locked( $cart_item_key, $product_id ) {
        if ( !is_user_logged_in() ) {
            return;
        }
        $user_id = get_current_user_id();
        if ( self::is_account_locked( $user_id ) ) {
            $message = esc_html__( 'Your account is currently locked. You cannot add items to the cart.', 'spending-limits-for-woocommerce' );
            if ( !wc_has_notice( $message, 'error' ) ) {
                wc_add_notice( $message, 'error' );
            }
            WC()->cart->remove_cart_item( $cart_item_key );
        }
    }

    /**
     * Prevent_checkout_if_locked
     *
     * @return void
     * @throws \Automattic\WooCommerce\StoreApi\Exceptions\RouteException When user is locked.
     */
    public static function prevent_checkout_if_locked() {
        if ( !is_user_logged_in() ) {
            return;
        }
        $user_id = get_current_user_id();
        if ( self::is_account_locked( $user_id ) ) {
            $message = esc_html__( 'Your account is currently locked. You cannot make purchases.', 'spending-limits-for-woocommerce' );
            if ( doing_action( 'woocommerce_store_api_checkout_update_order_from_request' ) ) {
                throw new \Automattic\WooCommerce\StoreApi\Exceptions\RouteException('over_spending_limit', esc_html( $message ), 400);
            }
            if ( !wc_has_notice( $message, 'error' ) ) {
                wc_add_notice( $message, 'error' );
            }
            remove_action( 'woocommerce_checkout_order_processed', 'woocommerce_order_data_store_cpt::create', 10 );
        }
    }

    /**
     * Prevent express checkout if locked
     *
     * @param  array $gateways available gateways.
     * @return array of available gateways
     */
    public static function remove_gateways_if_locked( $gateways ) {
        if ( !is_user_logged_in() ) {
            return $gateways;
        }
        $user_id = get_current_user_id();
        return ( self::is_account_locked( $user_id ) ? array() : $gateways );
    }

    /**
     * Message if no available payment methods because of lock
     *
     * @param  mixed $message message to show.
     * @return string
     */
    public function no_available_payment_message_if_locked( $message ) {
        if ( !is_user_logged_in() ) {
            return $message;
        }
        if ( $this->is_account_locked( get_current_user_id() ) ) {
            return esc_html__( 'Your account is locked. You cannot use any payment methods.', 'spending-limits-for-woocommerce' );
        }
        return $message;
    }

    /**
     * No available payment methods message if over limit
     *
     * @param  mixed $message message to show.
     * @return string
     */
    public function no_available_payment_message_if_over_limit( $message ) {
        if ( !is_user_logged_in() ) {
            return $message;
        }
        $user_id = get_current_user_id();
        $cart_total = $this->get_cart_total();
        if ( $this->should_apply_admin_limit( $user_id ) && $this->is_user_over_admin_limit( $user_id, $cart_total ) ) {
            /* translators: 1: amount, 2: period */
            return sprintf( esc_html__( 'You have reached your spending limit of %1$s per %2$s set by admin, or your order exceeds it.', 'spending-limits-for-woocommerce' ), wc_price( $this->get_option( 'enable_limit_for_all_value' ) ), esc_html( $this->get_option( 'enable_limit_for_all_period' ) ) );
        }
        $limit = $this->get_user_spend_limit_data( $user_id );
        if ( $limit['amount'] && $limit['period'] && $this->is_user_over_limit( $user_id, $cart_total ) ) {
            $period_label = $this->format_period_label( $limit['period'], $limit['custom_period'], $limit['custom_period_time'] );
            /* translators: 1: amount, 2: period */
            return sprintf( esc_html__( 'You have reached your spending limit of %1$s per %2$s, or your order exceeds it.', 'spending-limits-for-woocommerce' ), wc_price( $limit['amount'] ), esc_html( $period_label ) );
        }
        return $message;
    }

    /**
     * Show_lock_notice
     *
     * @return void
     */
    public static function show_lock_notice() {
        if ( !is_user_logged_in() ) {
            return;
        }
        $user_id = get_current_user_id();
        $lock_until = get_user_meta( $user_id, 'spending_limit_lock_until', true );
        if ( !self::is_account_locked( $user_id ) ) {
            return;
        }
        if ( 'permanent' === $lock_until ) {
            $message = esc_html__( 'Your account is permanently locked. You cannot make purchases.', 'spending-limits-for-woocommerce' );
        } else {
            $dateformat = get_option( 'date_format' );
            $timeformat = get_option( 'time_format' );
            /* translators: date */
            $message = sprintf( esc_html__( 'Your account is currently locked. You cannot make purchases until %s.', 'spending-limits-for-woocommerce' ), date_i18n( $dateformat, strtotime( $lock_until ) ) . ' ' . date_i18n( $timeformat, strtotime( $lock_until ) ) );
        }
        if ( !wc_has_notice( $message, 'notice' ) ) {
            wc_add_notice( $message, 'notice' );
        }
    }

    /**
     * Calculate spent from orders
     *
     * @param  mixed $user_id user id.
     * @param  mixed $time_period time period.
     * @return int
     */
    public static function calculate_spent_from_orders( $user_id, $time_period ) {
        global $wpdb;
        $log_time = self::get_last_limit_date_from_logs( $user_id );
        if ( $log_time ) {
            $time_part = gmdate( 'H:i:s', strtotime( $log_time ) );
            $now = time();
            $today_time = strtotime( gmdate( 'Y-m-d', $now ) . ' ' . $time_part );
            if ( $now >= $today_time ) {
                $limit = gmdate( 'U', strtotime( $time_period, $today_time ) + 86400 );
            } else {
                $limit = gmdate( 'U', strtotime( $time_period, $today_time ) );
            }
        } else {
            $limit = gmdate( 'Y-m-d', strtotime( $time_period ) );
        }
        $orders = wc_get_orders( array(
            'customer_id'  => $user_id,
            'limit'        => -1,
            'status'       => array('completed', 'processing'),
            'date_created' => '>=' . $limit,
        ) );
        $total_spent = 0.0;
        if ( $orders ) {
            foreach ( $orders as $order ) {
                $total_spent += $order->get_total();
            }
        }
        return floatval( $total_spent );
    }

    /**
     * Get last limit date from logs
     *
     * @param  mixed $user_id user id.
     * @return string
     */
    public static function get_last_limit_date_from_logs( $user_id ) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'slfw_log';
        $log_time = $wpdb->get_var( $wpdb->prepare( "SELECT `date` FROM {$table_name}\r\n\t\t\t\tWHERE user_id = %d\r\n\t\t\t\t\tAND type IN (6, 3)\r\n\t\t\t\tORDER BY `date` DESC\r\n\t\t\t\tLIMIT 1", $user_id ) );
        return $log_time;
    }

    /**
     * Is user over limit
     *
     * @param  mixed $user_id user id.
     * @param  mixed $value value.
     * @return bool
     */
    public static function is_user_over_limit( $user_id, $value = 0 ) {
        $limit = ( new self('', '') )->get_user_spend_limit_data( $user_id );
        if ( !$limit['amount'] || !$limit['period'] ) {
            return false;
        }
        $amount = $limit['amount'];
        $period = $limit['period'];
        $time_period = ( 'custom' === $period ? '-' . $limit['custom_period'] . ' ' . $limit['custom_period_time'] : '-1 ' . $period );
        $total_spent = self::calculate_spent_from_orders( $user_id, $time_period );
        return floatval( $total_spent ) + floatval( $value ) >= floatval( $amount );
    }

    /**
     * Is user over admin limit
     *
     * @param  mixed $user_id user id.
     * @param  mixed $value value.
     * @return bool
     */
    public function is_user_over_admin_limit( $user_id, $value = 0 ) {
        $spend_limit_amount = $this->get_option( 'enable_limit_for_all_value' );
        $spend_limit_period = $this->get_option( 'enable_limit_for_all_period' );
        if ( !$spend_limit_amount || !$spend_limit_period ) {
            return false;
        }
        $time_period = '-1 ' . $spend_limit_period;
        $total_spent = self::calculate_spent_from_orders( $user_id, $time_period );
        return floatval( $total_spent ) + floatval( $value ) > floatval( $spend_limit_amount );
    }

    /**
     * Is user limit over admin limit
     *
     * @param  mixed $user_id user id.
     * @return bool
     */
    public function is_user_limit_over_admin_limit( $user_id ) {
        return $this->calculate_per_day_amount_set_by_admin() < $this->calculate_per_day_amount_set_by_user( $user_id );
    }

    /**
     * Calculate per day amount
     *
     * @param  mixed $amount amount.
     * @param  mixed $period period.
     * @param  mixed $custom_period custom_period.
     * @param  mixed $custom_period_time custom_period_time.
     * @return float
     */
    public function calculate_per_day_amount(
        $amount,
        $period,
        $custom_period = 0,
        $custom_period_time = 'weeks'
    ) {
        $days = $this->period_to_days( $period, $custom_period, $custom_period_time );
        return ( $days > 0 ? floatval( $amount ) / $days : 0 );
    }

    /**
     * Calculate per day amount set by admin
     *
     * @return float
     */
    public function calculate_per_day_amount_set_by_admin() {
        return $this->calculate_per_day_amount( $this->get_option( 'enable_limit_for_all_value' ), $this->get_option( 'enable_limit_for_all_period' ) );
    }

    /**
     * Calculate per day amount set by user
     *
     * @param  mixed $user_id user id.
     * @return float
     */
    public function calculate_per_day_amount_set_by_user( $user_id = false ) {
        if ( !$user_id ) {
            $user_id = get_current_user_id();
        }
        $limit = $this->get_user_spend_limit_data( $user_id );
        return $this->calculate_per_day_amount(
            $limit['amount'],
            $limit['period'],
            $limit['custom_period'],
            $limit['custom_period_time']
        );
    }

    /**
     * Disable checkout button if limit exceeded
     *
     * @return mixed
     */
    public function disable_checkout_button_if_limit_exceeded() {
        if ( !is_user_logged_in() ) {
            return;
        }
        $user_id = get_current_user_id();
        $cart_total = $this->get_cart_total();
        if ( $this->is_account_locked( $user_id ) ) {
            $this->remove_checkout_buttons();
            return;
        }
        if ( $this->should_apply_admin_limit( $user_id ) && $this->is_user_over_admin_limit( $user_id, $cart_total ) ) {
            $this->remove_checkout_buttons();
            return;
        }
        $limit = $this->get_user_spend_limit_data( $user_id );
        if ( $limit['amount'] && $limit['period'] && $this->is_user_over_limit( $user_id, $cart_total ) ) {
            $this->remove_checkout_buttons();
        }
    }

}
