<?php

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


if ( !class_exists( 'Pricely_Product' ) ) {

    class Pricely_Product {

        private static $instance;
        private $taxonomy_util;
        private $product_util;
        private $parent_products;
        private $variations;
        private $variation_ids;

        public static function get_instance(): self {

            if ( !self::$instance ) {

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

            return self::$instance;
        }

        public function __construct() {

            $this->parent_products = array();
            $this->variations = array();
            $this->variation_ids = array();
        }

        public function get_product( $product_id ) {

            return $this->get_parent( $product_id );
        }

        public function get_variation_ids_by_id( $product_id ) {

            return $this->get_product_variation_ids_by_id( $product_id );
        }

        public function get_variation_by_id( $product_id, $variation_id ) {

            return $this->get_product_variation_by_id( $product_id, $variation_id );
        }

        public function get_product_key( $product_id, $variation_id ) {

            return $product_id . ':' . $variation_id;
        }

        public function get_product_id_data_by_key( $key ) {

            if ( empty( $key ) ) {

                return false;
            }

            $split_data = explode( ':', $key );

            if ( count( $split_data ) != 2 ) {

                return false;
            }

            return array(
                'product_id' => $split_data[ 0 ],
                'variation_id' => $split_data[ 1 ],
            );
        }

        public function get_cache_data( $product_id, $variation_id ) {

            $data_key = $this->get_product_key( $product_id, $variation_id );

            return array(
                'key' => $data_key,
                'product_id' => $product_id,
                'variation_id' => $variation_id,
            );
        }

        public function get_data( $product, $variation = null ) {

            $parent_id = $product->get_parent_id();

            if ( $parent_id > 0 && 'variation' == $product->get_type() ) {

                $parent_product = $this->get_parent( $parent_id );

                if ( !$parent_product ) {

                    return array();
                }

                return $this->get_all_data( $parent_product, $product );
            } else {

                return $this->get_all_data( $product, $variation );
            }
        }

        private function get_all_data( $product, $variation ) {

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

            if ( $variation ) {

                $variation_id = $variation->get_id();
            }

            $data_key = $this->get_product_key( $product_id, $variation_id );

            if ( !isset( $this->parent_products[ $product_id ] ) ) {

                $this->parent_products[ $product_id ] = $product;
            }

            if ( $variation && !isset( $this->variations[ $product_id ][ $variation_id ] ) ) {

                $this->variations[ $product_id ][ $variation_id ] = $variation;
            }

            if ( isset( $this->product_data[ $data_key ] ) ) {

                return $this->product_data[ $data_key ];
            }

            $data = array(
                'key' => $data_key
            );

            $this->product_data[ $data_key ] = $this->get_product_data( $data, $product, $variation );

            return $this->product_data[ $data_key ];
        }

        private function get_product_data( $data, $product, $variation ) {

            $props = array(
                'type' => $product->get_type(),
                'product_id' => $product->get_id(),
                'variation_id' => 0,
            );

            if ( $variation ) {

                $props[ 'variation_id' ] = $variation->get_id();
            }

            if ( has_filter( 'pricely_get-product-properties' ) ) {

                $props = apply_filters( 'pricely_get-product-properties', $props, $product, $variation );
            }

            return $this->get_prices( $this->combine_el( $data, $props ), $product, $variation );
        }

        private function get_prices( $data, $product, $variation ) {

            $prices = array();

            if ( $variation ) {

                $prices[ 'regular_price' ] = $variation->get_regular_price( 'edit' );
                $prices[ 'sale_price' ] = $variation->get_sale_price( 'edit' );
            } else {

                $prices[ 'regular_price' ] = $product->get_regular_price( 'edit' );
                $prices[ 'sale_price' ] = $product->get_sale_price( 'edit' );
            }

            if ( has_filter( 'pricely_get-product-prices' ) ) {

                $prices = apply_filters( 'pricely_get-product-prices', $prices, $product, $variation );
            }

            return $this->get_tax_data( $this->combine_el( $data, $prices ), $product, $variation );
        }

        private function get_tax_data( $data, $product, $variation ) {

            $tax_data = array(
                'tax_status' => $product->get_tax_status( 'edit' ),
                'tax_class' => $product->get_tax_class( 'edit' ),
            );
            
            if ( $variation ) {

                $tax_data[ 'tax_class' ] = $variation->get_tax_class( 'edit' );
            }

            $tax_data[ 'is_taxable' ] = $tax_data[ 'tax_status' ] === 'taxable' && wc_tax_enabled();

            if ( has_filter( 'pricely_get-product-tax-data' ) ) {

                $tax_data = apply_filters( 'pricely_get-product-tax-data', $tax_data, $product, $variation );
            }

            return $this->get_terms( $this->combine_el( $data, $tax_data ), $product, $variation );
        }

        private function get_terms( $data, $product, $variation ) {

            foreach ( $this->get_taxonomies() as $taxonomy => $taxonomy_data ) {

                $use_variations = $taxonomy_data[ 'use_variations' ];
                $terms_key = $taxonomy_data[ 'terms_key' ];

                $terms_data[ $terms_key ] = $this->get_taxonomy_terms( $product, $variation, $taxonomy, $use_variations );
            }

            if ( has_filter( 'pricely_get-product-terms' ) ) {

                $terms_data = apply_filters( 'pricely_get-product-terms', $terms_data, $product, $variation );
            }

            return $this->combine_el( $data, $terms_data );
        }

        private function combine_el( $data, $new_data ) {

            foreach ( $new_data as $key => $new_item ) {

                $data[ $key ] = $new_item;
            }

            return $data;
        }

        private function get_product_variation_by_id( $product_id, $variation_id ) {

            if ( isset( $this->variations[ $product_id ][ $variation_id ] ) ) {

                return $this->variations[ $product_id ][ $variation_id ];
            }

            $variation = wc_get_product( $variation_id );

            if ( $variation ) {

                $this->variations[ $product_id ][ $variation_id ] = $variation;
            }

            if ( isset( $this->variations[ $product_id ][ $variation_id ] ) ) {

                return $this->variations[ $product_id ][ $variation_id ];
            }

            return null;
        }

        private function get_product_variation_ids_by_id( $product_id ) {

            if ( isset( $this->variation_ids[ $product_id ] ) ) {

                return $this->variation_ids[ $product_id ];
            }

            $product = $this->get_parent( $product_id );

            $this->variation_ids[ $product_id ] = $product->get_children();

            return $this->variation_ids[ $product_id ];
        }

        private function get_parent( $parent_id ) {

            if ( isset( $this->parent_products[ $parent_id ] ) ) {
                return $this->parent_products[ $parent_id ];
            }

            $this->parent_products[ $parent_id ] = wc_get_product( $parent_id );

            return $this->parent_products[ $parent_id ];
        }
        
        private function get_taxonomies() {

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

                $this->taxonomy_util = Pricely_Taxonomy_Util::get_instance();
            }

            $taxonomies = array();

            foreach ( $this->taxonomy_util->get_taxonomies() as $taxonomy => $taxonomy_data ) {

                $taxonomies[ $taxonomy ] = array(
                    'terms_key' => $taxonomy_data[ 'terms_key' ],
                    'use_variations' => in_array( 'variations', $taxonomy_data[ 'supports' ] )
                );
            }

            return $taxonomies;
        }

        private function get_taxonomy_terms( $product, $variation, $taxonomy, $use_variations ) {

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

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

            return $this->product_util->get_terms( $product, $variation, $taxonomy, $use_variations );
        }

    }

}