<?php

if ( !defined( 'ABSPATH' ) ) {

    exit;
}

if ( !class_exists( 'Pricely_Cart_Totals_Items_Total_Util' ) ) {

    class Pricely_Cart_Totals_Items_Total_Util {

        private $wc_tax_options;
        private $tax_rates;
        private $totals_util;

        public function __construct() {

            $this->wc_tax_options = false;
            $this->tax_rates = array();

            $this->totals_util = Pricely_Totals_Util::get_instance();
        }

        public function calculate_total( $total, $calc_args, $data ) {

            $total_args = $calc_args[ 'items_total' ];

            $item_keys = $calc_args[ 'item_keys' ];

            $item_totals = $this->calculate_items_total( $total_args, $item_keys, $data );

            if ( !is_array( $item_totals ) ) {

                return $total;
            }

            $item_taxes = $this->calculate_item_taxes( $item_totals, $data );

            $final_total = array_sum( $item_totals );
            $final_tax_total = array_sum( $item_taxes );

            if ( $this->is_prices_include_tax() ) {

                $final_total = (array_sum( $item_totals ) - $final_tax_total);
            }

            if ( 'no' == $total_args[ 'tax_total' ] ) {

                return $total + $final_total;
            }

            return $total + $final_total + $final_tax_total;
        }

        private function calculate_items_total( $total_args, $item_keys, $data ) {

            if ( !isset( $data[ 'wc' ][ 'items' ] ) ) {

                return false;
            }

            $items_total = array();

            foreach ( array_keys( $data[ 'wc' ][ 'items' ] ) as $item_key ) {

                $based_on = $total_args[ 'based_on' ];

                $items_total[ $item_key ] = $this->get_item_total( $item_key, $data, $based_on );
            }

            if ( !count( $items_total ) ) {

                return false;
            }

            return $items_total;
        }

        private function calculate_item_taxes( $item_totals, $data ) {

            $item_taxes = array();

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

                return $item_taxes;
            }

            foreach ( $item_totals as $item_key => $total ) {

                if ( !isset( $data[ 'wc' ][ 'items' ][ $item_key ] ) ) {

                    continue;
                }

                if ( 0 == $total ) {

                    continue;
                }

                $item_tax_class = $this->get_item_tax_class( $item_key, $data );

                if ( false === $item_tax_class ) {

                    continue;
                }

                $item_tax_total = $this->calculate_tax( $total, $item_tax_class );

                $item_taxes[ $item_key ] = $item_tax_total;
            }

            return $item_taxes;
        }

        private function get_item_total( $item_key, $data, $based_on ) {

            $item_price = $this->get_item_price( $item_key, $data, $based_on );

            if ( $item_price == 0 ) {

                return 0;
            }

            $item_qty = $this->get_item_qty( $item_key, $data );

            return $item_price * $item_qty;
        }

        private function get_item_price( $item_key, $data, $based_on ) {

            if ( !isset( $data[ 'source_prices' ][ $item_key ] ) ) {

                return 0;
            }

            $source_prices = $data[ 'source_prices' ][ $item_key ];

            $price = 0;

            if ( 'sale_price' == $based_on && isset( $source_prices[ 'sale_price' ] ) ) {

                $price = $source_prices[ 'sale_price' ];
            } else if ( isset( $source_prices[ 'regular_price' ] ) ) {

                $price = $source_prices[ 'regular_price' ];
            }

            if ( is_numeric( $price ) ) {

                return ( float ) $price;
            }

            return 0;
        }

        private function get_item_qty( $item_key, $data ) {

            if ( isset( $data[ 'items_in_cart' ][ $item_key ][ 'quantity' ] ) ) {

                return $data[ 'items_in_cart' ][ $item_key ][ 'quantity' ];
            }

            return 1;
        }

        private function get_item_tax_class( $item_key, $data ) {

            if ( !isset( $data[ 'wc' ][ 'items' ][ $item_key ] ) ) {

                return false;
            }

            $item = $data[ 'wc' ][ 'items' ][ $item_key ];

            if ( 'taxable' !== $item[ 'tax_status' ] ) {

                return false;
            }

            return $item[ 'tax_class' ];
        }

        private function calculate_tax( $total, $tax_class ) {

            if ( false === $tax_class ) {

                return 0;
            }

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

                return 0;
            }

            $taxes = array_map(
                    array( $this->totals_util, 'round' ),
                    WC_Tax::calc_tax(
                            $total,
                            $this->get_tax_rates( $tax_class ),
                            $this->is_prices_include_tax()
                    )
            );

            return $this->process_taxes( $taxes );
        }

        private function get_tax_rates( $tax_class ) {

            if ( isset( $this->tax_rates[ $tax_class ] ) ) {

                return $this->tax_rates[ $tax_class ];
            }

            $this->tax_rates[ $tax_class ] = WC_Tax::get_rates( $tax_class );

            return $this->tax_rates[ $tax_class ];
        }

        private function process_taxes( $taxes ) {

            $taxes_total = 0;

            foreach ( $taxes as $tax ) {

                $taxes_total += $tax;
            }

            return $taxes_total;
        }

        private function is_prices_include_tax() {

            $tax_options = $this->get_wc_tax_options();

            return $tax_options[ 'prices_include_tax' ];
        }

        private function is_tax_enabled() {

            $tax_options = $this->get_wc_tax_options();

            return $tax_options[ 'tax_enabled' ];
        }

        private function get_wc_tax_options() {

            if ( $this->wc_tax_options ) {

                return $this->wc_tax_options;
            }

            $this->wc_tax_options = array(
                'tax_enabled' => wc_tax_enabled(),
                'prices_include_tax' => wc_prices_include_tax(),
            );

            return $this->wc_tax_options;
        }

    }

}

