<?php

if ( !defined( 'ABSPATH' ) ) {

    exit;
}

if ( !class_exists( 'Pricely_Pipeline_View_Cart' ) ) {

    require_once 'pipeline-view-mini-cart.php';
    require_once 'pipeline-view-checkout.php';

    class Pricely_Pipeline_View_Cart {

        private static $instance;
        private $display_prices_including_tax;
        private $item_is_taxables;
        private $product_util;
        private $view_mini_cart;
        private $view_checkout;
        private $shop_prices;
        private $any_prices;
        private $calc_prices;
        private $cart_item_prices;
        private $cart_item_totals;
        private $items_data;
        private $currency;

        public static function get_instance(): self {

            if ( !self::$instance ) {

                self::$instance = new self();
            }

            return self::$instance;
        }

        public function __construct() {

            $this->cart_item_prices = array();
            $this->cart_item_totals = array();
            $this->calc_prices = array();
            $this->item_is_taxables = array();

            $this->view_mini_cart = Pricely_Pipeline_View_Mini_Cart::get_instance();
            $this->view_checkout = Pricely_Pipeline_View_Checkout::get_instance();

            add_filter( 'woocommerce_cart_item_price', array( $this, 'get_cart_item_price' ), 99999, 3 );
            add_filter( 'woocommerce_cart_item_subtotal', array( $this, 'get_cart_item_subtotal' ), 99999, 3 );
        }

        public function get_cart_item_price( $theme_price, $cart_item, $cart_item_key ) {

            if ( !$this->can_show_prices() ) {

                return $theme_price;
            }

            $item_key = $this->get_item_key( $cart_item );

            if ( false === $item_key ) {

                return $theme_price;
            }

            $cart_item_prices = $this->process_cart_item_prices( $cart_item, $item_key );

            if ( false === $cart_item_prices ) {

                return $theme_price;
            }

            if ( $this->is_mini_cart() ) {

                return $this->view_mini_cart->get_cart_item_price( $theme_price, $cart_item_prices, $item_key );
            }

            $view_data = array(
                'view_id' => 'cart-item-prices',
                'item_key' => $item_key,
                'prices' => $cart_item_prices[ 'prices' ],
                'tooltip' => $cart_item_prices[ 'tooltip' ]
            );

            ob_start();

            $rendered = $this->render_view( $view_data );

            $rendered_content = ob_get_clean();

            if ( $rendered ) {

                return $rendered_content;
            }

            return $theme_price;
        }

        public function get_cart_item_subtotal( $theme_subtotal, $cart_item, $cart_item_key ) {

            if ( !$this->can_show_totals() ) {

                return $theme_subtotal;
            }

            $item_key = $this->get_item_key( $cart_item );

            if ( false === $item_key ) {

                return $theme_subtotal;
            }

            $cart_item_total = $this->process_cart_item_total( $cart_item, $item_key );

            if ( false === $cart_item_total ) {

                return $theme_subtotal;
            }

            if ( $this->is_checkout() ) {

                return $this->view_checkout->get_cart_item_total( $theme_subtotal, $cart_item_total, $item_key );
            }


            $view_data = array(
                'view_id' => 'cart-item-subtotal',
                'item_key' => $item_key,
                'new_total' => $cart_item_total[ 'new_total' ],
                'tax_label' => $cart_item_total[ 'tax_label' ],
            );

            ob_start();

            $rendered = $this->render_view( $view_data );

            $rendered_content = ob_get_clean();

            if ( $rendered ) {

                return $rendered_content;
            }

            return $theme_subtotal;
        }

        private function process_cart_item_prices( $cart_item, $item_key ) {

            if ( isset( $this->cart_item_prices[ $item_key ] ) ) {

                return $this->cart_item_prices[ $item_key ];
            }

            $shop_price = $this->get_shop_price( $item_key );

            if ( false === $shop_price ) {

                $this->cart_item_prices[ $item_key ] = false;

                return false;
            }

            $calculated_prices = $this->get_calculated_prices( $item_key, $shop_price );

            if ( !$calculated_prices ) {

                $this->cart_item_prices[ $item_key ] = false;

                return false;
            }

            $qty_prices = $this->process_qty_prices( $calculated_prices, $shop_price );

            if ( false === $qty_prices ) {

                $this->cart_item_prices[ $item_key ] = false;

                return false;
            }

            $prices = array();

            if ( count( $qty_prices ) == 1 ) {

                $prices = $this->process_simple_prices( $qty_prices, $cart_item, $item_key );
            } else {

                $prices = $this->process_many_prices( $qty_prices, $cart_item, $item_key );
            }

            if ( !count( $prices ) ) {

                $this->cart_item_prices[ $item_key ] = false;

                return false;
            }

            if ( !$prices ) {

                $this->cart_item_prices[ $item_key ] = false;

                return false;
            }

            $item_prices = array(
                'prices' => $prices,
                'tooltip' => $calculated_prices[ 'tooltip' ]
            );

            $this->cart_item_prices[ $item_key ] = apply_filters( 'pricely_get-pipeline-cart-item-prices', $item_prices, $item_key );

            return $this->cart_item_prices[ $item_key ];
        }

        private function process_cart_item_total( $cart_item, $item_key ) {

            if ( isset( $this->cart_item_totals[ $item_key ] ) ) {

                return $this->cart_item_totals[ $item_key ];
            }

            $shop_price = $this->get_shop_price( $item_key );

            if ( false === $shop_price ) {

                $this->cart_item_totals[ $item_key ] = false;

                return false;
            }

            $calculated_prices = $this->get_calculated_prices( $item_key, $shop_price );

            if ( !$calculated_prices ) {

                $this->cart_item_totals[ $item_key ] = false;

                return false;
            }

            $final_price = $this->get_final_price( $calculated_prices, $shop_price );

            if ( false === $final_price ) {

                $this->cart_item_totals[ $item_key ] = false;

                return false;
            }

            $item_qty = $calculated_prices[ 'item_qty' ];

            $totals = array(
                'total' => false,
                'new_total' => false,
                'tax_label' => false,
            );

            $new_total = $final_price * $item_qty;

            $totals[ 'new_total' ] = $this->get_display_price( $cart_item, $new_total, $item_key );

            if ( $this->get_item_is_taxable( $cart_item, $item_key ) ) {

                $totals[ 'tax_label' ] = $this->get_tax_label();
            }

            $this->cart_item_totals[ $item_key ] = apply_filters( 'pricely_get-pipeline-cart-item-totals', $totals, $item_key );

            return $this->cart_item_totals[ $item_key ];
        }

        private function process_simple_prices( $qty_prices, $cart_item, $item_key ) {

            if ( !isset( $qty_prices[ 0 ] ) ) {

                return false;
            }

            $qty_price = $qty_prices[ 0 ];

            $item_price = array(
                'qty' => false,
                'price' => false,
                'new_price' => false
            );

            $item_price[ 'new_price' ] = $this->get_display_price( $cart_item, $qty_price[ 'price' ], $item_key );

            if ( $this->is_mini_cart() ) {

                $item_price[ 'qty' ] = $qty_price[ 'qty' ];
            }

            $item_prices = array();

            $item_prices[] = $item_price;

            return $item_prices;
        }

        private function process_many_prices( $qty_prices, $cart_item, $item_key ) {

            $item_prices = array();

            foreach ( $qty_prices as $qty_price ) {

                $item_price = array(
                    'qty' => $qty_price[ 'qty' ],
                    'price' => false,
                    'new_price' => $this->get_display_price( $cart_item, $qty_price[ 'price' ], $item_key )
                );

                $item_prices[] = $item_price;
            }

            return $item_prices;
        }

        private function get_display_price( $cart_item, $price, $item_key ) {

            if ( !isset( $cart_item[ 'data' ] ) ) {

                return false;
            }

            if ( !$this->get_item_is_taxable( $cart_item, $item_key ) ) {

                return $price;
            }

            $product = $cart_item[ 'data' ];

            $args = array(
                'qty' => 1,
                'price' => $price,
            );

            if ( is_null( $this->display_prices_including_tax ) ) {

                $this->display_prices_including_tax = WC()->cart->display_prices_including_tax();
            }

            if ( $this->display_prices_including_tax ) {

                return wc_get_price_including_tax( $product, $args );
            }

            return wc_get_price_excluding_tax( $product, $args );
        }

        private function process_qty_prices( $calculated_prices, $shop_price ) {

            $qty_prices = array();

            if ( $calculated_prices[ 'item_qty' ] > $calculated_prices[ 'qty' ] ) {

                $qty_prices[ $shop_price ] = array(
                    'qty' => $calculated_prices[ 'item_qty' ] - $calculated_prices[ 'qty' ],
                    'price' => $shop_price,
                    'is_new' => false
                );
            }

            foreach ( $calculated_prices[ 'prices' ] as $price ) {

                $is_new = true;

                if ( $shop_price == $price ) {

                    $is_new = false;
                }

                if ( !isset( $qty_prices[ $price ] ) ) {

                    $qty_prices[ $price ] = array(
                        'qty' => 0,
                        'price' => $price,
                        'is_new' => $is_new
                    );
                }

                $qty_prices[ $price ][ 'qty' ] += 1;
            }

            if ( !count( $qty_prices ) ) {

                return false;
            }

            uasort( $qty_prices, array( $this, 'sort_qty_prices' ) );

            return array_values( $qty_prices );
        }

        private function get_calculated_prices( $item_key, $shop_price ) {

            if ( isset( $this->calc_prices[ $item_key ] ) ) {

                return $this->calc_prices[ $item_key ];
            }

            if ( is_null( $this->items_data ) ) {

                $this->items_data = Pricely_Pipeline_Cart_Prices::get_instance()->get_items_data();
            }

            if ( !isset( $this->items_data[ 'items' ][ $item_key ] ) ) {

                $this->calc_prices[ $item_key ] = false;

                return false;
            }

            $calculated_prices = $this->items_data[ 'items' ][ $item_key ];

            if ( isset( $calculated_prices[ 'prices' ] ) ) {

                $calculated_prices[ 'prices' ] = $this->convert_amounts( $calculated_prices[ 'prices' ], 'pipeline-cart-view-item-prices' );
            }

            $final_price = $this->get_final_price( $calculated_prices, $shop_price );

            if ( false === $final_price ) {

                $this->calc_prices[ $item_key ] = false;

                return false;
            }

            $pricing_id = $calculated_prices[ 'pricing_id' ];

            $tooltip = false;

            if ( isset( $this->items_data[ 'tooltips' ][ $pricing_id ] ) ) {

                $tooltip = $this->items_data[ 'tooltips' ][ $pricing_id ];
            }

            $calculated_prices[ 'tooltip' ] = $tooltip;

            $this->calc_prices[ $item_key ] = $calculated_prices;

            return $this->calc_prices[ $item_key ];
        }

        private function get_shop_price( $item_key ) {

            if ( is_null( $this->shop_prices ) ) {

                $shop_prices = Pricely_Pipeline_Cart_Prices::get_instance()->get_shop_prices();

                $this->shop_prices = apply_filters( 'pricely_get-pipeline-cart-item-shop-prices', $shop_prices );
            }

            if ( isset( $this->shop_prices[ $item_key ] ) ) {

                return $this->shop_prices[ $item_key ];
            }

            return $this->get_any_price( $item_key );
        }

        private function get_any_price( $item_key ) {

            if ( is_null( $this->any_prices ) ) {

                $any_prices = Pricely_Pipeline_Cart_Prices::get_instance()->get_any_prices();

                $this->any_prices = apply_filters( 'pricely_get-pipeline-cart-item-any-prices', $any_prices );
            }

            if ( isset( $this->any_prices[ $item_key ] ) ) {

                return $this->any_prices[ $item_key ];
            }

            return false;
        }

        private function get_final_price( $calculated_prices, $shop_price ) {

            $item_qty = $calculated_prices[ 'item_qty' ];
            $qty = $calculated_prices[ 'qty' ];

            $original_total = 0;

            if ( $item_qty > $qty ) {

                $original_total = ($item_qty - $qty) * $shop_price;
            }

            $total = array_sum( $calculated_prices[ 'prices' ] );

            $final_price = ($total + $original_total) / $item_qty;

            return $final_price;
        }

        private function get_item_key( $cart_item ) {

            if ( is_null( $this->product_util ) ) {

                $this->product_util = Pricely_Product::get_instance();
            }

            if ( !isset( $cart_item[ 'product_id' ] ) ) {

                return false;
            }

            if ( !isset( $cart_item[ 'variation_id' ] ) ) {

                return false;
            }

            return $this->product_util->get_product_key( $cart_item[ 'product_id' ], $cart_item[ 'variation_id' ] );
        }

        private function render_view( $view_data ) {

            return Pricely_Views::get_instance()->render_view( $view_data );
        }

        private function can_show_prices() {

            $settings = $this->get_settings();

            if ( $this->is_mini_cart() && 'no' != $settings[ 'mini_cart_item_prices' ] ) {

                return true;
            }

            if ( !$this->is_mini_cart() && 'no' != $settings[ 'cart_item_prices' ] ) {

                return true;
            }

            return false;
        }

        private function can_show_totals() {

            $settings = $this->get_settings();

            if ( $this->is_checkout() && 'no' != $settings[ 'checkout_item_prices' ] ) {

                return true;
            }

            if ( !$this->is_checkout() && 'no' != $settings[ 'cart_item_totals' ] ) {

                return true;
            }

            return false;
        }

        private function get_settings() {

            $default = array(
                'cart_item_prices' => 'reduced',
                'mini_cart_item_prices' => 'reduced',
                'cart_item_totals' => 'reduced',
                'checkout_item_prices' => 'reduced'
            );

            return Pricely::get_instance()->get_options( 'checkout_pricing_settings', $default );
        }

        private function sort_qty_prices( $qty_price_a, $qty_price_b ) {

            if ( !$qty_price_a[ 'is_new' ] ) {

                return -1;
            }

            if ( !$qty_price_b[ 'is_new' ] ) {

                return 1;
            }

            if ( $qty_price_a[ 'price' ] > $qty_price_b[ 'price' ] ) {

                return 1;
            }

            if ( $qty_price_a[ 'price' ] < $qty_price_b[ 'price' ] ) {

                return -1;
            }

            return 0;
        }

        private function get_tax_label() {

            if ( is_null( $this->display_prices_including_tax ) ) {

                $this->display_prices_including_tax = WC()->cart->display_prices_including_tax();
            }

            if ( $this->display_prices_including_tax ) {

                if ( !wc_prices_include_tax() && WC()->cart->get_subtotal_tax() > 0 ) {

                    return WC()->countries->inc_tax_or_vat();
                }
            } else {

                if ( wc_prices_include_tax() && WC()->cart->get_subtotal_tax() > 0 ) {

                    return WC()->countries->ex_tax_or_vat();
                }
            }

            return false;
        }

        private function get_item_is_taxable( $cart_item, $item_key ) {

            if ( isset( $this->item_is_taxables[ $item_key ] ) ) {

                return $this->item_is_taxables[ $item_key ];
            }

            if ( !isset( $cart_item[ 'data' ] ) ) {

                return false;
            }

            $product = $cart_item[ 'data' ];

            $this->item_is_taxables[ $item_key ] = $product->is_taxable();

            return $this->item_is_taxables[ $item_key ];
        }

        private function is_mini_cart() {

            return $this->view_mini_cart->is_mini_cart();
        }

        private function is_checkout() {

            return $this->view_checkout->is_checkout();
        }

        private function convert_amounts( $amounts, $amount_id ) {

            if ( is_null( $this->currency ) ) {

                $this->currency = Pricely_Currency::get_instance();
            }

            return $this->currency->convert_amounts( $amounts, $amount_id );
        }

    }

    Pricely_Pipeline_View_Cart::get_instance();
}