<?php  
use WooLentor\Traits\Singleton;
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly

class Woolentor_Backorder extends WC_Product{
   use Singleton;

    /**
     * Constructor
     */
    function __construct(){

        // Frontend scripts
        add_action('wp_enqueue_scripts', [ $this, 'enqueue_scripts' ] );

        // Admin scripts
        add_action('admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] );

        // Save order meta data while placing order
        add_action( 'woocommerce_checkout_create_order_line_item', [ $this, 'line_item_save' ], 99, 4 );

        // Through limit notice for product details page
        add_filter( 'woocommerce_add_cart_item_data', [ $this, 'render_single_product_notice' ], 99, 4 );

        // Through limit notice for cart page
        add_action('woocommerce_check_cart_items', [ $this, 'check_cart_item_backorder_limit' ] );

        // Add and save meta fields
        add_action( 'woocommerce_product_options_stock_status', [ $this, 'add_product_meta_fields' ] );
        add_action( 'woocommerce_process_product_meta', [ $this, 'save_product_metabox'], 10, 2 );

        // Render backorder availability text on product page
        add_filter('woocommerce_get_availability_text', [ $this, 'filter_get_availability_text'], 10, 2 );

        // Render backorder label to the cart page
        add_filter( 'woocommerce_get_item_data', [ $this, 'render_backorder_availability_cart_page' ], 99, 2 );

        // Meta data display
        add_filter( 'woocommerce_order_item_get_formatted_meta_data', [ $this, 'item_get_formatted_meta_data' ], 10, 4 );

         // Order Received page and Email template
         add_action( 'woocommerce_order_item_meta_end', [ $this, 'order_item_meta_end' ], 10, 4 );

    }


    /**
     * Enqueue scripts
     */
    public function enqueue_scripts(){
        // if( is_cart() || is_checkout()){
            wp_enqueue_style( 'woolentor-backorder', plugin_dir_url( __FILE__ ) . 'assets/css/backorder.css', [], WOOLENTOR_VERSION, 'all' );
        // }
    }

    /**
     * Enqueue scripts admin
     */
    public function admin_enqueue_scripts(){
        global $typenow;

        if( $typenow == 'product' ){
            wp_enqueue_style( 'woolentor-backorder-admin', plugin_dir_url( __FILE__ ) . 'assets/css/backorder-admin.css', [], WOOLENTOR_VERSION, 'all' );
            wp_enqueue_script( 'woolentor-backorder-admin', plugin_dir_url( __FILE__ ) . 'assets/js/backorder-admin.js', array('jquery'), WOOLENTOR_VERSION, true );
        }
    }

    /**
     * Save line items custom metadata
     * Line items refers to the individual item of an order
     */
    public function line_item_save( $item, $cart_item_key, $values, $order ) {
        $product = $values['data'];

        if( $product->is_on_backorder($values['quantity']) ){
            $backorder_qty = $values['quantity'] - max( 0, $product->get_stock_quantity() );
            $item->add_meta_data( 'woolentor_backordered', $backorder_qty, true );
        }
    }

    /**
     * Manage Order Item in thank you page || admin order page
     *
     * @param [array] $formatted_meta
     * @param [type] $item
     * @return void
     */
    public function item_get_formatted_meta_data( $formatted_meta, $item ) {

		foreach ( $formatted_meta as $key => $meta ) {
			if ( $meta->key == 'woolentor_backordered' ) {
                $meta->display_key  = esc_html__('Backordered','woolentor');
			}
		}

		return $formatted_meta;
	}

    /**
     * Looks through the cart to check each item is within backorder limit. If not, add an error.
     */
    public function check_cart_item_backorder_limit() {
        $result = true;
        $error  = new WP_Error();

        foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
            $product_data = $values['data'];
            $variation_id = '';
            $product_id   = '';

            if( $product_data->is_type('variation') ){
                $variation_id = $product_data->get_id();
            } else {
                $product_id = $product_data->get_id();
            }

            if( 
                !$product_data->managing_stock() && $product_data->get_stock_status() == 'onbackorder' || 
                 $product_data->managing_stock() && $product_data->backorders_allowed()
            ){

                $limit_status = $this->get_limit_crossed_status( $product_id, $variation_id, 0 );

                if( $limit_status ){
                    $can_buy_max = ((int) $limit_status['backorder_limit'] + (int) $limit_status['stock_qty']) - (int) $limit_status['qty_already_backordered'];

                    $error->add( 'woolentor_out_of_backorder_limit', sprintf( __( 'Sorry, "%s" has reached its maximum backorder limit. Orders can be placed for up to <b>%s</b> units.', 'woolentor' ), $product_data->get_name(), $can_buy_max ) );

                    $result = $error;
                }
            }
        }


        if ( is_wp_error( $result ) ) {
            wc_add_notice( $result->get_error_message(), 'error' );
            $return = false;
        }

        return $result;
    }

    /**
     * Render backorder limit notice for single product page
     */
    public function render_single_product_notice( $cart_item_data, $product_id, $variation_id, $quantity ){
        $product_id   = absint( $product_id );
        $variation_id = absint( $variation_id );

        // Ensure we don't add a variation to the cart directly by variation ID.
        if ( 'product_variation' === get_post_type( $product_id ) ) {
            $variation_id = $product_id;
            $product_id   = wp_get_post_parent_id( $variation_id );
        }

        $product_data = wc_get_product( $variation_id ? $variation_id : $product_id );

        $quantity     = apply_filters( 'woocommerce_add_to_cart_quantity', $quantity, $product_id );

        if( 
            !$product_data->managing_stock() && $product_data->get_stock_status() == 'onbackorder' || 
             $product_data->managing_stock() && $product_data->backorders_allowed()
        ){
            $limit_status = $this->get_limit_crossed_status($product_id, $variation_id, $quantity);

            if($limit_status ){

                $backorder_limit         = (int) $limit_status['backorder_limit'];
                $add_to_cart_qty         = $quantity;
                $stock_qty               = (int) $limit_status['stock_qty'];
                $qty_already_backordered = (int) $limit_status['qty_already_backordered'];
                $qty_already_on_cart     = (int) $limit_status['qty_on_cart'];

                $can_add_to_cart_max = ($backorder_limit + $stock_qty) - ($qty_already_backordered + $qty_already_on_cart);
                $can_add_to_cart_max = $can_add_to_cart_max < 0 ? 0 : $can_add_to_cart_max;

                // If this product already has on cart && user allowed add on "Cart" at least 1 qty
                if( $qty_already_on_cart > 0 ){

                    $message = sprintf(
                        '<a href="%s" class="button wc-forward">%s</a> %s',
                        wc_get_cart_url(),
                        __( 'View cart', 'woolentor' ),
                        /* translators: 1: quantity in stock 2: current quantity */
                        sprintf( __( 'Sorry, "%s" has reached its maximum backorder limit — (%s available). You already have %s in your cart.', 'woolentor' ), $product_data->get_name(), $can_add_to_cart_max, $qty_already_on_cart )
                    );

                    $message = apply_filters( 'wlbackorder_cart_product_not_enough_stock_already_in_cart_message', $message, $product_data, $can_add_to_cart_max, $qty_already_on_cart );

                } else {
                    $message = sprintf( __( 'Sorry, "%s" was not added to cart because it has reached the maximum backorder limit. (%s available).', 'woolentor' ), $product_data->get_name(), $can_add_to_cart_max );

                    $message = apply_filters( 'wlbackorder_cart_product_not_enough_stock_message', $message, $product_data, $can_add_to_cart_max );
                }
                

                throw new Exception( $message ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
            }  

        }

        return $cart_item_data;
    }

    /**
     * Look for the respected product into the cart and get the total quantities if it is available to the cart table
     */
    public function get_qty_in_cart( $product_id = '', $variation_id = '' ){
        $product_id   = absint( $product_id );
        $variation_id = absint( $variation_id );

        $p_id = $variation_id ? $variation_id : $product_id;
        $product_data = wc_get_product($p_id);

        if( $product_data->is_type('variation') && $product_data->managing_stock() == 'parent' ){
            $p_id = $product_data->get_parent_id();
        }

        $products_qty_in_cart = WC()->cart->get_cart_item_quantities();
        $qty_in_cart = !empty($products_qty_in_cart[$p_id]) ? $products_qty_in_cart[$p_id] : 0;

        return $qty_in_cart;
    }

    /**
     * Look for the respected product if it is already reached the backorder limit
     */
    public function get_limit_crossed_status( $product_id = '', $variation_id = '', $qty = 0 ){
        $product_id   = absint( $product_id );
        $variation_id = absint( $variation_id );

        // Ensure we don't add a variation to the cart directly by variation ID.
        if ( 'product_variation' === get_post_type( $product_id ) ) {
            $variation_id = $product_id;
            $product_id   = wp_get_post_parent_id( $variation_id );
        }

        $p_id            = $variation_id ? $variation_id : $product_id;
        $product_data    = wc_get_product( $p_id );
        $backorder_limit = (int) $this->get_option('backorder_limit', $product_id, $variation_id );

        // Get the order count of a product/variation product
        $qty_already_backordered = (int) $this->get_total_qty_already_backordered( $product_id, $variation_id );

        // Get qty count from the cart page
        $qty_on_cart = (int) $this->get_qty_in_cart( $product_id, $variation_id );

        // Sum with qty_already_backordered and qty_in_cart
        $qunatities = $qty + $qty_already_backordered + $qty_on_cart;

        // Deduct stock qty
        $stock_qty = $product_data->get_stock_quantity() > 0 ? $product_data->get_stock_quantity() : 0;

        if( $product_data->is_on_backorder($qty + $qty_on_cart) && $stock_qty ){
            $qunatities  = $qunatities - $stock_qty;
        }

        if( $qunatities > $backorder_limit ){
            return array(
                'status'                  => true,
                'backorder_limit'         => $backorder_limit,
                'qty_already_backordered' => $qty_already_backordered,
                'qty_on_cart'             => $qty_on_cart,
                'stock_qty'               => $stock_qty
            );
        }

        return false;
    }

    /**
     * Loop trhough the orders and get the total quantities ordered of the respected product
     */
    public function get_total_qty_already_backordered( $product_id = '', $variation_id = '' ){
        $product_id   = absint( $product_id );
        $variation_id = absint( $variation_id );
        $p_id         = $variation_id ? $variation_id : $product_id;

        $qty_already_backordered = 0;

        $status = wc_get_order_statuses();
        unset($status['wc-pending']);

        $limit = apply_filters('wlbackorder_order_query_limit', 250);

        $query = new WC_Order_Query( array(
            'status'  => array('wc-pending', 'wc-processing', 'wc-on-hold', 'wc-completed'),
            'limit'   => $limit,
            'orderby' => 'date',
            'order'   => 'DESC',
        ) );
        $orders = $query->get_orders();

        // Loop through all orders
        foreach($orders as $order){
            $line_items = $order->get_items();

            // Loop throgh on the current order items
            foreach($line_items as $item){
                $item_p_id = $item->get_variation_id() ? $item->get_variation_id() : $item->get_product_id();

                if( $p_id == $item_p_id ){

                    // check if this line item is under woolentor backorder
                    if($item->meta_exists('woolentor_backordered') && $item->get_meta('woolentor_backordered')){
                        $backorder_qty = (int) $item->get_meta('woolentor_backordered');
                        $qty_already_backordered += $backorder_qty;
                        break;
                    }
                }
            }
        }

        return $qty_already_backordered;
    }

    /**
     * Generate and return backorder availability message
     */
    public function get_availability_message( $product_id ){
        $availability_date = $this->get_option('backorder_availability_date', $product_id);
        $timestamp = strtotime($availability_date);
        if($timestamp){
           $availability_date = gmdate(get_option('date_format'), $timestamp); 
        }

        $backorder_limit   = $this->get_option('backorder_limit', $product_id);

        $availability_message = woolentor_get_option('backorder_availability_message', 'woolentor_backorder_settings');
        $availability_message = str_replace( '{availability_date}', '<span class="woolentor-backorder-availability">'.$availability_date.'</span>', $availability_message );

        if( $backorder_limit && $availability_date && empty($availability_message) ){
            $availability_message = __( 'On Backorder. Will be available on: '. $availability_date, 'woolentor' );
        }

        return $availability_message;
    }

    /**
     * Render the backorder availability message
     */
    public function filter_get_availability_text( $availability, $product ){

        $product_id = $product->get_id();
        if( $product->is_type('variation') ){
            $product_id = $product->get_parent_id();
        }

        $availability_message = $this->get_availability_message( $product_id ) ? $this->get_availability_message( $product_id ) : $availability;

        if ( $product->managing_stock() && $product->is_on_backorder( 1 ) ) {
            $availability = $product->backorders_require_notification() ? $availability_message : '';
        } elseif ( ! $product->managing_stock() && $product->is_on_backorder( 1 ) ) {
            $availability = $availability_message;
        }

        return $availability;
    }

    /**
     * Render the backorder availability message on cart page
     */
    public function render_backorder_availability_cart_page( $item_data, $cart_item ){

        $product_data = $cart_item['data'];

        if($product_data->is_type('simple')){
            $product_id = $product_data->get_id();
        } elseif( $product_data->is_type('variation') ){
            $product_id = $product_data->get_parent_id();
        }

        if( $product_data->is_on_backorder() ){
            $item_data[] = array(
                'name'      => 'woolentor_cart_backorder_availability',
                'display'   => '<p class="woolentor-backorder-notification backorder_notification">'.wp_kses_post($this->get_availability_message( $product_id )).'</p>',
                'value'     => 'wc_woolentor_back_order_content',
            );
        }

        return $item_data;

    }

    /**
     * Order Item Meta
     *
     * @param [int] $item_id
     * @param [array] $item
     * @param [object] $order
     * @param [html] $plain_tex
     * @return void
     */
    public function order_item_meta_end( $item_id, $item, $order, $plain_tex ){
        
        $product_data = $item->get_product();

        if($product_data->is_type('simple')){
            $product_id = $product_data->get_id();
        } elseif( $product_data->is_type('variation') ){
            $product_id = $product_data->get_parent_id();
        }

        if( $product_data->is_on_backorder() ){
            echo '<p class="woolentor-backorder-notification backorder_notification">';
            echo wp_kses_post($this->get_availability_message( $product_id ));
            echo '</p>';
        }
        
    }


    /**
     * Get the option value either from metadata or the global settings
     */
    public function get_option( $option_name = '', $product_id = '', $variation_id = '' ){

        $product_id   = absint( $product_id );
        $variation_id = absint( $variation_id );
        $p_id         = $variation_id ? $variation_id : $product_id;

        $product_data = wc_get_product( $p_id );
        if( $product_data->is_type('variation') ){
            $product_id = $product_data->get_parent_id();
        }

        $global_settings_value =  woolentor_get_option( $option_name, 'woolentor_backorder_settings');
        $meta_value            = get_post_meta( $product_id, '_woolentor_'. $option_name, true );

        if( $meta_value ){
            $global_settings_value = $meta_value;
        }

        return $global_settings_value;
    }

    /**
     * Render backorder meta fields into the inventory tab
     */
    public function add_product_meta_fields(){
        $product_id = get_the_id();
        $product    = wc_get_product($product_id);

        $backorder_limit_global = woolentor_get_option('backorder_limit', 'woolentor_backorder_settings');
        $backorder_limit_global = $backorder_limit_global ? __( "Store-wide backorder limit ($backorder_limit_global)", "woolentor") : '';

        $availability_date_global = woolentor_get_option('backorder_availability_date', 'woolentor_backorder_settings');
        $availability_date_global = $availability_date_global ? __( "Store-wide availability ($availability_date_global)", "woolentor") : '';

        $backorder_limit        = get_post_meta( $product_id, '_woolentor_backorder_limit', true );
        $backorder_availability = get_post_meta( $product_id, '_woolentor_backorder_availability_date', true );

        // Stock status
        $manage_stock = get_post_meta($product_id, '_manage_stock', true);
        $stock_status = $product->is_type('simple') ? get_post_meta($product_id, '_stock_status', true) : '';
        $allow_backorder = get_post_meta($product_id, '_backorders', true);
        ?>

        <div class="woolentor-backorder-fields show_if_simple show_if_variable wl_manage_stock--<?php echo esc_attr($manage_stock); ?> wl_stock_status--<?php echo esc_attr($stock_status); ?>  wl_allow_backorder--<?php echo esc_attr($allow_backorder); ?>">

        <?php
        woocommerce_wp_text_input(
            array(
                'id'                => '_woolentor_backorder_limit',
                'value'             =>  $backorder_limit,
                'label'             => __( 'Backorder Limit', 'woolentor' ),
                'placeholder'       => $backorder_limit_global,
                'wrapper_class'     => '',
                'desc_tip'          => true,
                'description'       => __( 'Backorder limit. If this is a variable product this value will be used to control backorder limit for all variations, unless you define backorder limit at variation level.', 'woolentor' ),
                'type'              => 'number',
                'custom_attributes' => array(
                    'step' => 'any',
                ),
            )
        );
        ?>
            <p class="form-field">
                <label for="_woolentor_backorder_availability_date"><?php echo esc_html__('Backorder Availability', 'woolentor') ?></label>
                <?php echo wc_help_tip( esc_html__('The selected date will show as a message to customer. You can customize the message as you need from the module settings.', 'woolentor') ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
                <input type="date" class="short hasDatepicker" name="_woolentor_backorder_availability_date" id="_woolentor_backorder_availability_date" value="<?php echo esc_attr($backorder_availability); ?>" placeholder="<?php echo esc_attr($availability_date_global); ?>">
            </p>
        </div> <!-- .woolentor-backorder-fields -->
        <?php
    }

    /**
     * Save backorder meta fields into the inventory tab
     */
    public function save_product_metabox( $post_id, $post ){
        $posted_data     = wp_unslash( $_REQUEST );
        $backorder_limit = $posted_data['_woolentor_backorder_limit'];
        $backorder_availability = $posted_data['_woolentor_backorder_availability_date'];
        
        if( isset($posted_data['_woolentor_backorder_limit']) ){
            update_post_meta( $post_id, '_woolentor_backorder_limit', $backorder_limit );
        }

        if( isset($posted_data['_woolentor_backorder_availability_date']) ){
            update_post_meta( $post_id, '_woolentor_backorder_availability_date', $backorder_availability );
        }
    }
}

Woolentor_Backorder::instance();    