<?php

/**
 * DRBU_Cart_Handler — WooCommerce korpa / session / cene.
 *
 * Preuzima ulogu starog includes/discounts.php: dodaje podatke u cart item,
 * vraća ih iz session-a i menja cenu po komadu kroz DRBU_Pricing_Engine.
 *
 * @package discount-rules-box-upsell
 */
defined( 'ABSPATH' ) || exit;
class DRBU_Cart_Handler {
    /**
     * Singleton instanca.
     *
     * @var DRBU_Cart_Handler|null
     */
    private static $instance = null;

    /**
     * Vraća jedinu instancu.
     *
     * @return DRBU_Cart_Handler
     */
    public static function instance() {
        if ( null === self::$instance ) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Privatan konstruktor.
     */
    private function __construct() {
        add_filter(
            'woocommerce_add_cart_item_data',
            array($this, 'add_cart_item_data'),
            10,
            3
        );
        add_filter(
            'woocommerce_get_cart_item_from_session',
            array($this, 'get_cart_item_from_session'),
            10,
            2
        );
        add_action(
            'woocommerce_before_calculate_totals',
            array($this, 'before_calculate_totals'),
            10,
            1
        );
        add_filter(
            'woocommerce_cart_item_name',
            array($this, 'cart_item_name'),
            10,
            3
        );
        add_action(
            'woocommerce_checkout_create_order_line_item',
            array($this, 'save_order_item_meta'),
            10,
            4
        );
    }

    /**
     * Dodaje podatke o paketu u cart item kada se proizvod dodaje preko
     * buy box-a (?drbu_pack=N). Provera is_purchasable() štiti od zloupotrebe.
     *
     * @param array $cart_item_data Postojeći podaci.
     * @param int   $product_id     ID proizvoda.
     * @param int   $variation_id   ID varijacije.
     * @return array
     */
    public function add_cart_item_data( $cart_item_data, $product_id, $variation_id ) {
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        if ( empty( $_GET['drbu_pack'] ) ) {
            return $cart_item_data;
        }
        $product = wc_get_product( $product_id );
        if ( !$product || !$product->is_purchasable() ) {
            return $cart_item_data;
        }
        $settings = DRBU_Settings::instance();
        $tiers = $settings->tiers();
        // 3-ći nivo (paket) je Pro funkcija — podrazumevano ostaje na 2, i
        // samo drbu_fs()->is__premium_only() grana (koju Freemius Deployment
        // izbacuje iz Free ZIP-a) sme ikad da pročita realnu vrednost iz
        // podešavanja i da je proveri protiv žive licence.
        $tier_count = 2;
        if ( function_exists( 'drbu_fs' ) && drbu_fs() && drbu_fs()->is__premium_only() ) {
            $tier_count = $this->resolve_tier_count__premium_only( $settings );
        }
        $index = max( 1, min( $tier_count, (int) $_GET['drbu_pack'] ) );
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $cart_item_data['drbu_tier_index'] = $index;
        // Originalna veličina paketa (1/2/3) u trenutku dodavanja u korpu —
        // koristi se u before_calculate_totals() da se cena za model_c
        // (fiksna cena paketa) skalira sa stvarnom količinom u korpi umesto
        // da ostane konstantna bez obzira koliko kupac naknadno izmeni polje
        // za količinu na strani korpe (vidi tier_total()/model_c u
        // class-drbu-pricing-engine.php za detalje zašto je ovo neophodno).
        $cart_item_data['drbu_tier_qty'] = ( isset( $tiers[$index - 1]['qty'] ) ? max( 1, (int) $tiers[$index - 1]['qty'] ) : $index );
        $cart_item_data['drbu_discount_type'] = $settings->get( 'drbu_discount_type', 'percentage' );
        $cart_item_data['drbu_pricing_model'] = ( isset( $_GET['drbu_pm'] ) ? sanitize_text_field( wp_unslash( $_GET['drbu_pm'] ) ) : $settings->get( 'drbu_pricing_model', 'model_a' ) );
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $cart_item_data['drbu_original_price'] = (float) $product->get_price();
        // Popusti se UVEK čitaju iz server-side konfiguracije, nikad iz GET
        // parametara. Ranije se ovde direktno verovalo $_GET['drbu_d2'] /
        // $_GET['drbu_d3'], što je svakom posetiocu omogućavalo da u URL
        // ubaci proizvoljan procenat popusta (npr.
        // ?add-to-cart=X&drbu_pack=2&drbu_d2=99) i dobije proizvod skoro
        // besplatno, bez obzira na podešeni popust ili Pro status sajta.
        $cart_item_data['drbu_d2'] = ( isset( $tiers[1]['discount'] ) ? (float) $tiers[1]['discount'] : 0 );
        $cart_item_data['drbu_d3'] = ( isset( $tiers[2]['discount'] ) ? (float) $tiers[2]['discount'] : 0 );
        return $cart_item_data;
    }

    /**
     * Vraća podatke o paketu iz session-a.
     *
     * @param array $cart_item    Cart item u korpi.
     * @param array $session_data Podaci iz session-a.
     * @return array
     */
    public function get_cart_item_from_session( $cart_item, $session_data ) {
        if ( empty( $session_data['drbu_tier_index'] ) ) {
            return $cart_item;
        }
        $keys = array(
            'drbu_tier_index',
            'drbu_tier_qty',
            'drbu_discount_type',
            'drbu_pricing_model',
            'drbu_original_price',
            'drbu_d2',
            'drbu_d3'
        );
        foreach ( $keys as $key ) {
            if ( isset( $session_data[$key] ) ) {
                $cart_item[$key] = $session_data[$key];
            }
        }
        return $cart_item;
    }

    /**
     * Preračunava cenu po komadu pre izračunavanja ukupnih iznosa korpe.
     *
     * @param \WC_Cart $cart Korpa.
     */
    public function before_calculate_totals( $cart ) {
        if ( is_admin() && !defined( 'DOING_AJAX' ) ) {
            return;
        }
        if ( !is_a( $cart, 'WC_Cart' ) ) {
            return;
        }
        if ( !apply_filters( 'drbu_pack_discounts_enabled', true ) ) {
            return;
        }
        $engine = DRBU_Pricing_Engine::instance();
        $settings = DRBU_Settings::instance();
        foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
            if ( empty( $cart_item['drbu_tier_index'] ) ) {
                continue;
            }
            $original = ( isset( $cart_item['drbu_original_price'] ) ? (float) $cart_item['drbu_original_price'] : 0 );
            if ( $original <= 0 ) {
                $product = ( isset( $cart_item['data'] ) ? $cart_item['data'] : null );
                $original = ( $product && is_callable( array($product, 'get_price') ) ? (float) $product->get_price() : 0 );
            }
            if ( $original <= 0 ) {
                continue;
            }
            $qty = max( 1, (int) $cart_item['quantity'] );
            // Podrazumevano klampovano na tier 1/2 (Free); samo
            // drbu_fs()->is__premium_only() grana (izbačena iz Free ZIP-a od
            // strane Freemius Deployment-a) sme da pusti realan treći nivo.
            $tier_index = min( (int) $cart_item['drbu_tier_index'] - 1, 1 );
            if ( function_exists( 'drbu_fs' ) && drbu_fs() && drbu_fs()->is__premium_only() ) {
                $tier_index = $this->resolve_tier_index__premium_only( (int) $cart_item['drbu_tier_index'] - 1 );
            }
            $discount_type = ( isset( $cart_item['drbu_discount_type'] ) ? $cart_item['drbu_discount_type'] : 'percentage' );
            $pricing_model = ( isset( $cart_item['drbu_pricing_model'] ) ? $cart_item['drbu_pricing_model'] : $settings->get( 'drbu_pricing_model', 'model_a' ) );
            // Originalna veličina paketa (npr. 3 za "3-pack, fiksna cena
            // $39.99") u trenutku dodavanja u korpu — NIJE isto što i $qty
            // iznad, koji je trenutna, korisnički izmenljiva količina na
            // strani korpe. model_c (fiksna cena paketa) ovo koristi da
            // skalira cenu proporcionalno broju paketa u korpi, umesto da
            // tretira fiksnu cenu kao konstantu bez obzira na izmenjenu
            // količinu (vidi tier_total()/model_c za detalje).
            $tier_qty = ( isset( $cart_item['drbu_tier_qty'] ) ? max( 1, (int) $cart_item['drbu_tier_qty'] ) : $tier_index + 1 );
            $tiers = array(array(
                'discount' => 0,
                'qty'      => $tier_qty,
            ), array(
                'discount' => ( isset( $cart_item['drbu_d2'] ) ? (float) $cart_item['drbu_d2'] : 0 ),
                'qty'      => $tier_qty,
            ), array(
                'discount' => ( isset( $cart_item['drbu_d3'] ) ? (float) $cart_item['drbu_d3'] : 0 ),
                'qty'      => $tier_qty,
            ));
            $unit_price = $engine->unit_price(
                $original,
                $qty,
                $tiers,
                $discount_type,
                $tier_index,
                $pricing_model
            );
            $line_total = $unit_price * $qty;
            $cart_item['data']->set_price( $unit_price );
            // Eksplicitno postavljanje linijskih totala sprečava druge
            // dodatke/teme da vrate regularnu cenu.
            if ( isset( $cart->cart_contents[$cart_item_key] ) ) {
                $cart->cart_contents[$cart_item_key]['line_total'] = $line_total;
                $cart->cart_contents[$cart_item_key]['line_subtotal'] = $line_total;
            }
        }
    }

    /**
     * Prikazuje labelu paketa i uštedu u korpi ispod naziva proizvoda.
     *
     * @param string $name          HTML naziva proizvoda.
     * @param array  $cart_item     Cart item.
     * @param string $cart_item_key Ključ cart item-a.
     * @return string
     */
    public function cart_item_name( $name, $cart_item, $cart_item_key ) {
        if ( empty( $cart_item['drbu_tier_index'] ) || false !== strpos( $name, 'drbu-cart-item-meta' ) ) {
            return $name;
        }
        $settings = DRBU_Settings::instance();
        $strings = $settings->strings();
        $engine = DRBU_Pricing_Engine::instance();
        $original = ( isset( $cart_item['drbu_original_price'] ) ? (float) $cart_item['drbu_original_price'] : 0 );
        if ( $original <= 0 ) {
            $product = ( isset( $cart_item['data'] ) ? $cart_item['data'] : null );
            $original = ( $product && is_callable( array($product, 'get_price') ) ? (float) $product->get_price() : 0 );
        }
        if ( $original <= 0 ) {
            return $name;
        }
        $qty = max( 1, (int) $cart_item['quantity'] );
        // Isto klampovanje kao u before_calculate_totals() — bez ovoga, ako
        // Pro licenca istekne dok je stavka već u korpi, tekst "ušteda" bi
        // mogao da prikaže popust za treći nivo koji se stvarno više ne
        // naplaćuje (before_calculate_totals() već klampuje stvarnu cenu).
        $tier_index = min( (int) $cart_item['drbu_tier_index'] - 1, 1 );
        if ( function_exists( 'drbu_fs' ) && drbu_fs() && drbu_fs()->is__premium_only() ) {
            $tier_index = $this->resolve_tier_index__premium_only( (int) $cart_item['drbu_tier_index'] - 1 );
        }
        $discount_type = ( isset( $cart_item['drbu_discount_type'] ) ? $cart_item['drbu_discount_type'] : 'percentage' );
        $pricing_model = ( isset( $cart_item['drbu_pricing_model'] ) ? $cart_item['drbu_pricing_model'] : $settings->get( 'drbu_pricing_model', 'model_a' ) );
        // Vidi napomenu u before_calculate_totals() — potrebno i ovde da
        // prikazana ušteda za model_c ostane tačna posle izmene količine.
        $tier_qty = ( isset( $cart_item['drbu_tier_qty'] ) ? max( 1, (int) $cart_item['drbu_tier_qty'] ) : $tier_index + 1 );
        $tiers = array(array(
            'discount' => 0,
            'qty'      => $tier_qty,
        ), array(
            'discount' => ( isset( $cart_item['drbu_d2'] ) ? (float) $cart_item['drbu_d2'] : 0 ),
            'qty'      => $tier_qty,
        ), array(
            'discount' => ( isset( $cart_item['drbu_d3'] ) ? (float) $cart_item['drbu_d3'] : 0 ),
            'qty'      => $tier_qty,
        ));
        $label = ( isset( $strings['cart_pack_label'] ) ? $strings['cart_pack_label'] : '' );
        $label = str_replace( '{qty}', (string) $qty, $label );
        $saving = $engine->savings(
            $original,
            $qty,
            $tiers,
            $discount_type,
            $tier_index,
            $pricing_model
        );
        $meta_items = array();
        if ( !empty( $label ) ) {
            $meta_items[] = sprintf( '<span class="drbu-cart-pack"><strong>%s</strong></span>', esc_html( $label ) );
        }
        if ( 'yes' === $settings->get( 'drbu_enable_savings', 'yes' ) && $saving > 0.005 ) {
            $savings_text = ( isset( $strings['savings_label'] ) ? $strings['savings_label'] : __( 'savings', 'drbu-discount-rules-box-for-woocommerce' ) );
            $meta_items[] = sprintf( '<span class="drbu-cart-savings">%s %s</span>', esc_html( $savings_text ), wp_kses_post( wc_price( $saving ) ) );
        }
        $html = $name;
        if ( !empty( $meta_items ) ) {
            $html .= '<div class="drbu-cart-item-meta" style="margin-top:4px; font-size:12.5px; line-height:1.4;">' . implode( ' &bull; ', $meta_items ) . '</div>';
        }
        return $html;
    }

    /**
     * Čuva meta podatke DRBU stavke i cele porudžbine.
     *
     * Za svaku stavku dodatu preko buy box-a upisuje:
     *  - _drbu_tier_index     indeks paketa (1, 2 ili 3)
     *  - _drbu_tier_qty       količina iz buy box-a
     *  - _drbu_original_price osnovna cena po komadu pre popusta
     *  - _drbu_pack_label     labela paketa
     * Na nivou porudžbine postavlja _drbu_order_upsell = 'yes' i ažurira
     * _drbu_max_tier_qty na najveću količinu paketa u narudžbini.
     *
     * @param \WC_Order_Item_Product $item          Stavka porudžbine.
     * @param string                 $cart_item_key Ključ u korpi.
     * @param array                  $values        Cart item podaci.
     * @param \WC_Order              $order         Porudžbina.
     */
    public function save_order_item_meta(
        $item,
        $cart_item_key,
        $values,
        $order
    ) {
        if ( empty( $values['drbu_tier_index'] ) ) {
            return;
        }
        $tier_index = (int) $values['drbu_tier_index'];
        $tier_qty = max( 1, (int) (( isset( $values['quantity'] ) ? $values['quantity'] : $tier_index )) );
        $item->update_meta_data( '_drbu_tier_index', $tier_index );
        $item->update_meta_data( '_drbu_tier_qty', $tier_qty );
        if ( isset( $values['drbu_original_price'] ) ) {
            $item->update_meta_data( '_drbu_original_price', (float) $values['drbu_original_price'] );
        }
        $settings = DRBU_Settings::instance();
        $strings = $settings->strings();
        $label = ( isset( $strings['cart_pack_label'] ) ? $strings['cart_pack_label'] : '' );
        if ( '' !== $label ) {
            $item->update_meta_data( '_drbu_pack_label', str_replace( '{qty}', (string) $tier_qty, $label ) );
        }
        $order->update_meta_data( '_drbu_order_upsell', 'yes' );
        $order->update_meta_data( '_drbu_max_tier_qty', max( (int) $order->get_meta( '_drbu_max_tier_qty' ), $tier_qty ) );
    }

}

/**
 * Backward compat: stara funkcija za cenu po komadu (procentni model).
 *
 * @deprecated 2.0.0 Koristite DRBU_Pricing_Engine::unit_price().
 *
 * @param float $base Osnovna cena.
 * @param int   $qty  Količina.
 * @param float $d2   Popust 2. komada (%).
 * @param float $d3   Popust 3. komada (%).
 * @return float
 */
function drbu_calculate_unit_price(
    $base,
    $qty,
    $d2,
    $d3
) {
    $engine = DRBU_Pricing_Engine::instance();
    $tiers = array(array(
        'discount' => 0,
    ), array(
        'discount' => (float) $d2,
    ), array(
        'discount' => (float) $d3,
    ));
    return $engine->unit_price(
        $base,
        $qty,
        $tiers,
        'percentage'
    );
}

/**
 * Backward compat: stara funkcija za ukupan iznos paketa.
 *
 * @deprecated 2.0.0 Koristite DRBU_Pricing_Engine::tier_total().
 *
 * @param float $base Osnovna cena.
 * @param int   $qty  Količina.
 * @param float $d2   Popust 2. komada (%).
 * @param float $d3   Popust 3. komada (%).
 * @return float
 */
function drbu_tier_total(
    $base,
    $qty,
    $d2,
    $d3
) {
    $engine = DRBU_Pricing_Engine::instance();
    $tiers = array(array(
        'discount' => 0,
    ), array(
        'discount' => (float) $d2,
    ), array(
        'discount' => (float) $d3,
    ));
    return $engine->tier_total(
        $base,
        $qty,
        $tiers,
        'percentage'
    );
}
