<?php
/**
 * The shipping helpers for class
 *
 * This is used to define shipping utility helpers for the plugin
 *
 * @since      1.8.8
 * @package    WCVendors_Pro
 */
class WCVendors_Pro_Shipping_Utils {
    /**
     * Get line items for current vendor.
     *
     * @param WC_Order $order The order object.
     * @param integer  $vendor_id The vendor ID.
     * @return array $line_items The line items for the vendor.
     * @version 1.8.8
     * @since   1.8.8
     */
    public static function get_line_items_for_vendor( $order, $vendor_id = 0 ) {
        $line_items = array();

        if ( ! $vendor_id ) {
            $vendor_id = get_current_user_id();
        }
        $order_items = $order->get_items();
        foreach ( $order_items as $item ) {
            $product_id     = $item->get_product_id() === 0 ? $item->get_variation_id() : $item->get_product_id();
            $product_author = (int) get_post_field( 'post_author', $product_id );

            if ( $product_author !== (int) $vendor_id ) {
                continue;
            }

            $line_items[ $item->get_id() ] = $item;
        }

        /**
         * Filter the line items for the vendor.
         *
         * @param array $line_items The line items for the vendor.
         * @param WC_Order $order The order object.
         * @param integer  $vendor_id The vendor ID.
         */
        return apply_filters(
            'wcvendors_pro_get_line_items_for_vendor',
            $line_items,
            $order,
            $vendor_id
        );
    }

    /**
     * Get shipping items for current vendor.
     *
     * @param WC_Order $order The order object.
     * @param integer  $vendor_id The vendor ID.
     * @return array $shipping_items The shipping items for the vendor.
     * @version 1.8.8
     * @since   1.8.8
     */
    public static function get_shipping_items_for_vendor( $order, $vendor_id = 0 ) {
        $give_shipping = wc_string_to_bool( get_option( 'wcvendors_vendor_give_shipping', 'no' ) );

        if ( ! $vendor_id ) {
            $vendor_id = get_current_user_id();
        }

        if ( ! $give_shipping ) {
            return array();
        }

        $shipping_products = self::get_shipping_products( $order );
        $vendor_shipping   = array();
        $shipping_items    = array();
        $line_items        = self::get_line_items_for_vendor( $order, $vendor_id );
        $total_qty         = 0;
        foreach ( $line_items as $item ) {
            foreach ( $shipping_products as $id => $products ) {
                if ( in_array( $item->get_name(), $products, true ) ) {
                    $total_qty        += $item->get_quantity();
                    $extra_data        = array(
                        'item_id'      => $id,
                        'product_id'   => $item->get_product_id() === 0 ? $item->get_variation_id() : $item->get_product_id(),
                        'quantity'     => $item->get_quantity(),
                        'line_item_id' => $item->get_id(),
                    );
                    $vendor_shipping[] = $extra_data;
                }
            }
        }

        foreach ( $vendor_shipping as $shipping_item ) {
            $new_shipping_item          = new WC_Order_Item_Shipping( $shipping_item['item_id'] );
            $shipping_item['total_qty'] = $total_qty;
            $new_shipping_item->add_meta_data( 'extra_data', $shipping_item, true );
            $shipping_items[] = $new_shipping_item;
        }

        /**
         * Filter the shipping items for the vendor.
         *
         * @param array   $shipping_items The shipping items for the vendor.
         * @param WC_Order $order The order object.
         *
         * @return array $shipping_items The shipping items for the vendor.
         */
        return apply_filters(
            'wcvendors_pro_get_shipping_items_for_vendor',
            $shipping_items,
            $order
        );
    }

    /**
     * Map order item shipping id with product name.
     *
     * @param WC_Order $order The order object.
     * @return array
     * @version 1.8.8
     * @since   1.8.8
     */
    public static function get_shipping_products( $order ) {
        $map = array();
        foreach ( $order->get_items( 'shipping' ) as $shipping ) {
            $shipping_products = $shipping->get_meta( 'shipping_products', true );
            if ( ! empty( $shipping_products ) ) {
                foreach ( $shipping_products as $product ) {
                    $map[ $shipping->get_id() ][] = $product['name'];
                }
            } else {
                $items         = $shipping->get_meta( __( 'Items', 'woocommerce' ), true );
                $replace       = preg_replace( '/(\s&times;\s\d+)/', '/', $items );
                $array_replace = explode( '/', rtrim( $replace, '/' ) );

                foreach ( $array_replace as $item ) {
                    $item                         = trim( $item, ', ' );
                    $map[ $shipping->get_id() ][] = $item;
                }
            }
        }

        /**
         * Filter the shipping products for the order.
         *
         * @param array  $map The mapped shipping products.
         * @param WC_Order $order The order object.
         * @return array $map The mapped shipping products.
         */
        return apply_filters(
            'wcvendors_pro_get_shipping_products_for_order',
            $map,
            $order
        );
    }

    /**
     * Get shiping cost for Vendor Shipping.
     *
     * @param WC_Order_Item_Shipping $shipping_item The shipping item object.
     * @param int                    $product_id The product ID.
     *
     * @return float $shipping_cost The shipping cost.
     * @version 1.8.8
     * @since   1.8.8
     */
    public static function get_vendor_shipping_cost( $shipping_item, $product_id ) {
        $shipping_cost = 0;

        $shipping_meta_data = $shipping_item->get_meta_data();

        foreach ( $shipping_meta_data as $meta ) {
            if ( 'vendor_costs' === $meta->key ) {
                $package_cost = $meta->value;
                foreach ( $package_cost['items'] as $key => $cost ) {
                    if ( (int) $product_id === (int) $cost['product_id'] ) {
                        $shipping_cost = $cost['shipping_cost'];
                        break;
                    }
                }
            }
        }

        return $shipping_cost;
    }

    /**
     * Get shipping cost for flat rate shipping.
     *
     * @param WC_Order_Item_Shipping $shipping_item The shipping item object.
     * @param array                  $vendor_shipping The vendor shipping data.
     * @param array                  $extra_data The extra data.
     *
     * @return float $shipping_cost The shipping cost.
     *
     * @version 1.8.8
     * @since   1.8.8
     */
    public static function get_flat_rate_shipping_cost( $shipping_item, $vendor_shipping, $extra_data ) {
        $shipping_cost     = 0;
        $product_id        = $extra_data['product_id'];
        $product           = wc_get_product( $product_id );
        $shipping_class_id = $product->get_shipping_class_id();
        $qty               = $extra_data['quantity'];
        $total_qty         = $extra_data['total_qty'];
        $shipping_method   = self::get_package_shipping_class( $vendor_shipping );

        if ( ! is_null( $shipping_method ) ) {
            $shipping_cost = self::get_flat_rate_shipping_cost_by_class( $shipping_item, $shipping_class_id, $qty, $total_qty, $vendor_shipping, $shipping_method );
        } else {
            $shipping_cost = self::get_flat_rate_shipping_cost_by_product( $shipping_item, $qty, $total_qty );
        }
        return $shipping_cost;
    }

    /**
     * Get shipping cost for flat rate shipping by shipping class.
     *
     * @param WC_Order_Item_Shipping $shipping_item The shipping item object.
     * @param int                    $shipping_class_id The shipping class ID.
     * @param int                    $qty The quantity.
     * @param int                    $total_qty The total quantity.
     * @param array                  $vendor_shipping The vendor shipping data.
     * @param WC_Shipping_Flat_Rate  $shipping_method The shipping method data.
     *
     * @return float $shipping_cost The shipping cost.
     *
     * @version 1.8.8
     * @since   1.8.8
     */
    public static function get_flat_rate_shipping_cost_by_class( $shipping_item, $shipping_class_id, $qty, $total_qty, $vendor_shipping, $shipping_method ) {
        $shipping_cost            = 0;
        $shipping_method_settings = $shipping_method->instance_settings;
        $class_cost_str           = "class_cost_{$shipping_class_id}";
        $flat_rate_cost           = $shipping_method_settings['cost'];
        $no_class_cost            = $shipping_method_settings['no_class_cost'];
        $class_cost_type          = $shipping_method->type;
        if ( 'class' === $class_cost_type ) {
            if ( isset( $shipping_method_settings[ $class_cost_str ] ) ) {
                $class_cost    = $shipping_method_settings[ $class_cost_str ];
                $shipping_cost = $class_cost;
            } else {
                $remain_cost   = $flat_rate_cost + $no_class_cost;
                $has_class_qty = self::get_shipping_class_qty( $vendor_shipping );
                $base_cost     = $remain_cost / ( $total_qty - $has_class_qty );
                $shipping_cost = $base_cost * $qty;
            }
        } else {
            $highest_class_cost = self::get_highest_class_cost( $vendor_shipping );

            $base_cost     = ( $flat_rate_cost + $highest_class_cost ) / $total_qty;
            $shipping_cost = $base_cost * $qty;
        }
        return $shipping_cost;
    }

    /**
     * Get shipping cost for flat rate shipping by product.
     *
     * @param WC_Order_Item_Shipping $shipping_item The shipping item object.
     * @param int                    $qty The quantity.
     * @param int                    $total_qty The total quantity.
     *
     * @return float $shipping_cost The shipping cost.
     *
     * @version 1.8.8
     * @since   1.8.8
     */
    public static function get_flat_rate_shipping_cost_by_product( $shipping_item, $qty, $total_qty ) {
        $shipping_cost            = 0;
        $shipping_class_instance  = $shipping_item->get_instance_id();
        $shipping_method          = WC_Shipping_Zones::get_shipping_method( $shipping_class_instance );
        $shipping_method_settings = $shipping_method->instance_settings;
        $shipping_method_cost     = $shipping_method_settings['cost'];
        $base_cost                = $shipping_method_cost / $total_qty;
        $shipping_cost            = $base_cost * $qty;
        return $shipping_cost;
    }

    /**
     * Get extra data for shipping item.
     *
     * @param WC_Order_Item_Shipping $shipping_item The shipping item object.
     *
     * @return array $extra_data The extra data.
     *
     * @version 1.8.8
     * @since   1.8.8
     */
    public static function get_extra_data( $shipping_item ) {
        $extra_data = array();
        $meta_data  = $shipping_item->get_meta_data();
        foreach ( $meta_data as $meta ) {
            if ( 'extra_data' === $meta->key ) {
                $extra_data = $meta->value;
                break;
            }
        }
        return $extra_data;
    }

    /**
     * Get shipping class for shipping item.
     *
     * @param array $vendor_shipping The vendor shipping data.
     * @return WC_Shipping_Flat_Rate $shipping_class The shipping class.
     *
     * @version 1.8.8
     * @since   1.8.8
     */
    public static function get_package_shipping_class( $vendor_shipping ) {
        $shipping_class = null;
        foreach ( $vendor_shipping as $shipping_item ) {
            $extra_data       = self::get_extra_data( $shipping_item );
            $product_id       = $extra_data['product_id'];
            $product          = wc_get_product( $product_id );
            $shiping_class_id = $product->get_shipping_class_id();
            if ( $shiping_class_id || 0 !== $shiping_class_id ) {
                $shipping_class_instance = $shipping_item->get_instance_id();
                $shipping_method         = WC_Shipping_Zones::get_shipping_method( $shipping_class_instance );
                $shipping_class          = $shipping_method;
                break;
            }
        }
        return $shipping_class;
    }

    /**
     * Get qty for shipping item that has class cost.
     *
     * @param array $vendor_shipping The vendor shipping data.
     *
     * @version 1.8.8
     * @since   1.8.8
     *
     * @return int $qty The quantity.
     */
    public static function get_shipping_class_qty( $vendor_shipping ) {
        $qty = 0;
        foreach ( $vendor_shipping as $shipping_item ) {
            $extra_data       = self::get_extra_data( $shipping_item );
            $product_id       = $extra_data['product_id'];
            $product          = wc_get_product( $product_id );
            $shiping_class_id = $product->get_shipping_class_id();
            if ( $shiping_class_id || 0 !== $shiping_class_id ) {
                $qty += $extra_data['quantity'];
            }
        }
        return $qty;
    }

    /**
     * Get highest class cost for shipping item.
     *
     * @param array $vendor_shipping The vendor shipping data.
     *
     * @version 1.8.8
     * @since   1.8.8
     *
     * @return float $highest_class_cost The highest class cost.
     */
    public static function get_highest_class_cost( $vendor_shipping ) {
        $highest_class_cost = 0;
        foreach ( $vendor_shipping as $shipping_item ) {
            $extra_data               = self::get_extra_data( $shipping_item );
            $product_id               = $extra_data['product_id'];
            $product                  = wc_get_product( $product_id );
            $shiping_class_id         = $product->get_shipping_class_id();
            $class_cost_str           = "class_cost_{$shiping_class_id}";
            $shipping_class_instance  = $shipping_item->get_instance_id();
            $shipping_method          = WC_Shipping_Zones::get_shipping_method( $shipping_class_instance );
            $shipping_method_settings = $shipping_method->instance_settings;
            if ( isset( $shipping_method_settings[ $class_cost_str ] ) ) {
                $class_cost = $shipping_method_settings[ $class_cost_str ];
                if ( $class_cost > $highest_class_cost ) {
                    $highest_class_cost = $class_cost;
                }
            }
        }
        return $highest_class_cost;
    }


    /**
     * Bypass woocommerce shipping cache
     *
     * @since 1.9.7
     */
    public static function bypass_woocommerce_shipping_cache() {

        if ( WC()->session && WC()->cart ) {
            $packages = WC()->cart->get_shipping_packages();
            foreach ( $packages as $key => $value ) {
                $shipping_session = "shipping_for_package_$key";
                unset( WC()->session->$shipping_session );
            }
        }

        WC_Cache_Helper::get_transient_version( 'shipping', true );
    }
}
