<?php

if ( !defined( 'ABSPATH' ) ) {
    exit;
}

if ( !class_exists( 'Pricely_Pipeline_Variation_Prices' ) ) {

    class Pricely_Pipeline_Variation_Prices {

        private static $instance;
        private $adjusted_prices_key;
        private $context;
        private $adjustment_ids;
        private $currency;

        public static function get_instance(): self {

            if ( !self::$instance ) {

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

            return self::$instance;
        }

        public function __construct() {

            $this->adjusted_prices_key = 'adjusted_prices';
            $this->context = 'shop_prices';
            $this->adjustment_ids = array();

            add_filter( 'woocommerce_variation_prices_sale_price', array( $this, 'get_variation_sale_price' ), 99999, 3 );
            add_filter( 'woocommerce_variation_prices_price', array( $this, 'get_variation_price' ), 99999, 3 );

            add_filter( 'woocommerce_get_variation_prices_hash', array( $this, 'get_variation_prices_hash' ), 99999, 3 );
        }

        public function get_variation_sale_price( $price, $variation, $product ) {

            if ( $this->is_pre_processing_items() ) {

                return $price;
            }

            $adjusted_sale_price = $this->get_adjusted_sale_price( $product, $variation );

            if ( false !== $adjusted_sale_price ) {

                return apply_filters( 'pricely_get-pipeline-variation-sale-price', $this->convert_amount( $adjusted_sale_price, 'pipeline-variation-sale-price' ), $product, $variation );
            }

            return $price;
        }

        public function get_variation_price( $price, $variation, $product ) {

            if ( $this->is_pre_processing_items() ) {

                return $price;
            }

            $adjusted_sale_price = $this->get_adjusted_sale_price( $product, $variation );

            if ( false === $adjusted_sale_price ) {

                $calculated_price = $this->get_calculated_price( $price, $product, $variation );
            } else {

                $adjusted_price = apply_filters( 'pricely_get-pipeline-variation-price', $this->convert_amount( $adjusted_sale_price, 'pipeline-variation-price' ), $product, $variation );

                $calculated_price = $this->get_calculated_price( $adjusted_price, $product, $variation );
            }

            if ( false !== $calculated_price ) {

                return apply_filters( 'pricely_get-pipeline-variation-price', $this->convert_amount( $calculated_price, 'pipeline-variation-price' ), $product, $variation );
            }

            if ( false !== $adjusted_sale_price ) {

                return apply_filters( 'pricely_get-pipeline-variation-price', $this->convert_amount( $adjusted_sale_price, 'pipeline-variation-price' ), $product, $variation );
            }

            return $price;
        }

        public function get_variation_prices_hash( $price_hash, $product, $for_display ) {

            $hash_data = array(
                'price_hash' => $price_hash,
                'revision' => $this->get_option_revision(),
                'adjustment_ids' => $this->adjustment_ids
            );

            return md5( wp_json_encode( $hash_data ) );
        }

        private function get_calculated_price( $price, $product, $variation ) {

            return Pricely_Pipeline_Cart_Prices::get_instance()->get_price( $price, $product, $variation );
        }

        private function get_adjusted_sale_price( $product, $variation ) {

            $adjusted_prices = $this->get_adjusted_prices( $product, $variation );

            if ( $adjusted_prices && isset( $adjusted_prices[ 'sale_price' ] ) ) {

                return $adjusted_prices[ 'sale_price' ];
            }

            return false;
        }

        private function get_adjusted_prices( $product, $variation ) {

            $cache_data = $this->get_product_cache( $product, $variation );

            if ( isset( $cache_data[ $this->adjusted_prices_key ] ) ) {

                $this->process_adjustment_ids( $cache_data[ $this->adjusted_prices_key ] );

                return $cache_data[ $this->adjusted_prices_key ];
            }

            $data = $this->get_product_data( $product, $variation );

            if ( isset( $data[ $this->adjusted_prices_key ] ) ) {

                $this->process_adjustment_ids( $data[ $this->adjusted_prices_key ] );

                return $data[ $this->adjusted_prices_key ];
            }

            return false;
        }

        private function get_product_cache( $product, $variation ) {

            $product_id = $product->get_id();
            $variation_id = $variation->get_id();

            $pipeline = Pricely_Pipeline::get_instance();

            return $pipeline->get_product_cache( $product_id, $variation_id, $this->context );
        }

        private function get_product_data( $product, $variation ) {

            $pipeline = Pricely_Pipeline::get_instance();

            $this->init_pipeline_data( $pipeline );

            return $pipeline->get_product_data( $product, $variation, $this->context );
        }

        private function init_pipeline_data( $pipeline ) {

            $pipeline->get_data( $this->context );
        }

        private function is_pre_processing_items() {

            return Pricely_Pipeline_Cart::get_instance()->is_pre_processing_items();
        }

        private function process_adjustment_ids( $adjusted_prices ) {

            if ( !isset( $adjusted_prices[ 'ids' ] ) ) {

                return;
            }

            foreach ( $adjusted_prices[ 'ids' ] as $key => $id ) {

                if ( !isset( $this->adjustment_ids[ $key ] ) ) {

                    $this->adjustment_ids[ $key ] = array();
                }

                if ( !in_array( $id, $this->adjustment_ids[ $key ] ) ) {

                    $this->adjustment_ids[ $key ][] = $id;
                }
            }
        }

        private function get_option_revision() {

            return Pricely::get_instance()->get_options( 'revision', 0 );
        }

        private function convert_amount( $amount, $amount_id ) {

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

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

            return $this->currency->convert_amount( $amount, $amount_id );
        }

    }

    Pricely_Pipeline_Variation_Prices::get_instance();
}
