<?php
/**
 * Description: ConnectPOS Integration with WooCommerce Product.
 * Author: ConnectPOS
 *
 * @package ConnectPOS\Integration
 */

namespace ConnectPOS\Integration;

use ConnectPOS\Helper\Cpos_Database;
use Iconic_WAS_Fees;
use WC_Product_Variation;
use WP_REST_Request;

/**
 * Class Cpos_Product
 * @package ConnectPOS\Integration
 */
class Cpos_Product {
    /**
     * @var CposDatabase
     */
    protected $db;

    /**
     * Per-request cache for the WooCommerce COGS feature toggle.
     *
     * @var bool|null
     */
    private $cpp_cogs_enabled_cache = null;

    /**
     * CposOrder constructor.
     *
     * @param Cpos_Database $db
     *
     * @return void
     */
    public function __construct( Cpos_Database $db )
    {
        $this->db = $db;

        add_filter('woocommerce_rest_product_object_query', array($this, 'cpp_product_date_time_filter'), 10, 2);

        add_filter('woocommerce_rest_prepare_product_object', array($this, 'cpp_product_variants'), 999, 1);

        add_filter('woocommerce_rest_prepare_product_variation_object', array($this, 'cpp_product_variants'), 20, 1);

        add_filter('woocommerce_rest_prepare_shop_order_object', array($this, 'cpp_order_date_time'), 20, 1);

        add_filter('woocommerce_rest_prepare_shop_order_refund_object', array($this, 'cpp_order_date_time'), 20, 1);
        add_action('wc_after_products_starting_sales', array($this, 'force_update_product_modified'), 20);
        add_action('wc_after_products_ending_sales', array($this, 'force_update_product_modified'), 20);
        add_action('rest_api_init', array($this, 'register_price_excl_tax_field'), 20);
    }

    /**
     * @return void
     */
    public function register_price_excl_tax_field() {
        register_rest_field( 'product', 'price_excl_tax', array(
            'get_callback' => array( $this, 'get_price_excl_tax' ),
        ) );
        register_rest_field( 'product_variation', 'price_excl_tax', array(
            'get_callback' => array( $this, 'get_price_excl_tax' ),
        ) );
    }

    /**
     * @param $obj
     * @return string|null
     */
    public function get_price_excl_tax( $obj ) {
        $product = wc_get_product( $obj['id'] );
        if ( ! $product ) {
            return null;
        }

        return (string) wc_get_price_excluding_tax( $product );
    }

    /**
     * @param $response
     * @param $object
     * @param $request
     *
     * @return mixed
     */
    public function cpp_product_variants( $response ) {
        $variations       = [];
        if ( isset( $response->data['variations'] ) ) {
            $variations = $response->data['variations'];
        }
        $variations_array = [];

        $product = wc_get_product($response->data['id']);

        if ( class_exists( 'Iconic_WAS_Fees' ) ) {
            $iconic_was_fees = Iconic_WAS_Fees::get_fees($product);

            if ( $iconic_was_fees ) {
                $response->data['iconic_was_fees'] = $iconic_was_fees;
            }
        }

        if ( empty( $variations ) || ! is_array( $variations ) ) {
            return $response;
        }

        // Resolve COGS context once per product (see get_variation_cogs_structure):
        // - skip entirely when the WooCommerce COGS feature is disabled;
        // - pre-load the parent's effective/total values so variations that inherit
        //   from the parent don't each re-resolve the parent object.
        $cogs_enabled          = $this->cpp_is_cogs_enabled();
        $parent_has_cogs       = $cogs_enabled && $product && method_exists( $product, 'get_cogs_effective_value' );
        $parent_cogs_effective = $parent_has_cogs ? (float) ( $product->get_cogs_effective_value() ?? 0 ) : 0;
        $parent_cogs_total     = $parent_has_cogs ? (float) ( $product->get_cogs_total_value() ?? 0 ) : 0;

        foreach ( $variations as $variation ) {
            $variation_id                    = $variation;
            $variation                       = new WC_Product_Variation( $variation_id );

            $variations_res                  = [];
            $variations_res['id']            = $variation_id;
            $variations_res['on_sale']       = $variation->is_on_sale();
            $variations_res['regular_price'] = (float) $variation->get_regular_price();
            $variations_res['sale_price']    = (float) $variation->get_sale_price();
            $variations_res['price_excl_tax'] = (string) wc_get_price_excluding_tax( $variation );
            $variations_res['sku']           = $variation->get_sku();
            $variations_res['quantity']      = $variation->get_stock_quantity();
            $variations_res['manage_stock']  = $variation->get_manage_stock();
            $variations_res['backorders']    = $variation->get_backorders();
            $variations_res['global_unique_id'] = $variation->get_global_unique_id();
            $variations_res['weight'] = $variation->get_weight();
            $variations_res['dimensions'] = [
                'length' => $variation->get_length(),
                'width' => $variation->get_width(),
                'height' => $variation->get_height(),
            ];
            // Add COGS (Cost of Goods Sold) - matches parent product structure with defined and effective values
            $variations_res['cost_of_goods_sold']    = $this->get_variation_cogs_structure( $variation, $cogs_enabled, $parent_cogs_effective, $parent_cogs_total );
            if ($variation->get_image_id() != 0) {
                $variationImageAttachment = wp_get_attachment_image_src($variation->get_image_id());
                if (!empty($variationImageAttachment)) {
                    $variations_res['image'] = $variationImageAttachment[0];
                }
            }
            if ( $variations_res['quantity'] === null ) {
                $variations_res['quantity'] = '';
            }
            $variations_res['stock'] = $variation->get_stock_quantity();

            $attributes = [];
            // variation attributes
            foreach ( $variation->get_variation_attributes() as $attribute_name => $attribute ) {
                // taxonomy-based attributes are prefixed with `pa_`, otherwise simply `attribute_`
                $attributes[] = [
                    'name'   => wc_attribute_label( str_replace( 'attribute_', '', $attribute_name ), $variation ),
                    'slug'   => str_replace( 'attribute_', '', wc_attribute_taxonomy_slug( $attribute_name ) ),
                    'option' => $attribute,
                ];
            }

            $variations_res['meta_data']             = $variation->get_meta_data();
            $variations_res['attributes']            = $attributes;
            $variations_res['date_on_sale_to']       = $variation->get_date_on_sale_to();
            $variations_res['date_on_sale_from']     = $variation->get_date_on_sale_from();
            $variations_array[]           = $variations_res;
        }

        $response->data['product_variations'] = $variations_array;

        return $response;
    }

    /**
     * @param object $response
     *
     * @return mixed
     */
    public function cpp_order_date_time( $response ) {
        if ( $response->data === null ) {
            return $response;
        }

        if ( ! empty( $response->data['date_modified_gmt'] ) ) {
            $response->data['date_modified'] = $response->data['date_modified_gmt'];
        }

        if ( ! empty( $response->data['date_created_gmt'] ) ) {
            $response->data['date_created'] = $response->data['date_created_gmt'];
        }

        if ( ! empty( $response->data['date_completed_gmt'] ) ) {
            $response->data['date_completed'] = $response->data['date_completed_gmt'];
        }

        if ( ! empty( $response->data['date_paid_gmt'] ) ) {
            $response->data['date_paid'] = $response->data['date_paid_gmt'];
        }

        return $response;
    }

    /**
     * @param array $args
     * @param WP_REST_Request $request
     *
     * @return array
     */
    public function cpp_product_date_time_filter( array $args, WP_REST_Request $request ) {
        $modified_after  = $request->get_param( 'modified_after_gmt' );
        $modified_before = $request->get_param( 'modified_before_gmt' );

        if ( $modified_after ) {
            $args['date_query'][0]['column'] = 'post_modified_gmt';
            $args['date_query'][0]['after']  = $modified_after;
        }

        if ( $modified_before ) {
            $args['date_query'][0]['column'] = 'post_modified_gmt';
            $args['date_query'][0]['before'] = $modified_before;
        }

        return $args;
    }

    /**
     * Get COGS (Cost of Goods Sold) structure for a product variation.
     *
     * Returns COGS data matching parent product structure using WooCommerce COGS API:
     * - defined_value: Custom COGS set on variation (null if inherited or not set)
     * - effective_value: Actual cost used (custom or inherited from parent)
     * - total_value: WooCommerce calculated total COGS value
     *
     * @param WC_Product_Variation $variation             The variation product object
     * @param bool                 $cogs_enabled          Whether the WC COGS feature is enabled
     * @param float                $parent_cogs_effective Parent product effective COGS (resolved once per product)
     * @param float                $parent_cogs_total     Parent product total COGS (resolved once per product)
     *
     * @return array COGS structure with values and total_value
     */
    private function get_variation_cogs_structure( WC_Product_Variation $variation, $cogs_enabled = false, $parent_cogs_effective = 0, $parent_cogs_total = 0 ) {
        // #1 Skip all COGS work when the feature is disabled or unavailable (v10.3+).
        if ( ! $cogs_enabled || ! method_exists( $variation, 'get_cogs_effective_value' ) ) {
            return $this->cpp_empty_cogs_structure();
        }

        // Get defined value (null if not set on this variation, will inherit from parent).
        $defined_value = $variation->get_cogs_value() ?? null;

        // #2 When the variation has no own value it inherits the parent's cost, so reuse
        // the parent values already resolved once per product instead of re-resolving the
        // parent for every variation. Only variations with their own value defer to the
        // WooCommerce API (parent object is already cached at this point).
        if ( $defined_value === null ) {
            $effective_value = $parent_cogs_effective;
            $total_value     = $parent_cogs_total;
        } else {
            $effective_value = (float) ( $variation->get_cogs_effective_value() ?? 0 );
            $total_value     = (float) ( $variation->get_cogs_total_value() ?? 0 );
        }

        return [
            'values' => [
                [
                    'defined_value'  => $defined_value,
                    'effective_value' => $effective_value
                ]
            ],
            'total_value' => $total_value
        ];
    }

    /**
     * Empty COGS structure returned when the feature is off/unavailable.
     *
     * @return array
     */
    private function cpp_empty_cogs_structure() {
        return [
            'values' => [
                [
                    'defined_value'  => null,
                    'effective_value' => 0
                ]
            ],
            'total_value' => 0
        ];
    }

    /**
     * Whether the WooCommerce "Cost of Goods Sold" feature is enabled (cached per request).
     *
     * @return bool
     */
    private function cpp_is_cogs_enabled() {
        if ( $this->cpp_cogs_enabled_cache !== null ) {
            return $this->cpp_cogs_enabled_cache;
        }

        $enabled = false;
        if ( class_exists( '\Automattic\WooCommerce\Utilities\FeaturesUtil' )
            && method_exists( '\Automattic\WooCommerce\Utilities\FeaturesUtil', 'feature_is_enabled' ) ) {
            $enabled = (bool) \Automattic\WooCommerce\Utilities\FeaturesUtil::feature_is_enabled( 'cost_of_goods_sold' );
        }

        $this->cpp_cogs_enabled_cache = $enabled;

        return $enabled;
    }

    /**
     * @param $product_ids
     * @return void
     */
    function force_update_product_modified( $product_ids ) {
        if (!is_array($product_ids) || empty($product_ids)) return;
        foreach ( $product_ids as $product_id ) {
            wp_update_post( array(
                'ID' => $product_id,
                'post_modified' => current_time( 'mysql' ),
                'post_modified_gmt' => current_time( 'mysql', 1 ),
            ) );
        }
    }
}