<?php
/**
 * Teleship Fulfillment Calculator
 *
 * Calculates fulfillment status based on shipped quantities.
 * Mirrors Magento's PartialFulfillmentCalculator.
 *
 * @package Teleship
 * @since 2.0.0
 */

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

/**
 * Class Teleship_Fulfillment_Calculator
 */
class Teleship_Fulfillment_Calculator {

    /**
     * Fulfillment statuses
     */
    const STATUS_UNFULFILLED = 'UNFULFILLED';
    const STATUS_PARTIALLY_FULFILLED = 'PARTIALLY_FULFILLED';
    const STATUS_FULFILLED = 'FULFILLED';

    /**
     * Epsilon for floating-point comparison
     */
    const FLOAT_EPSILON = 0.0001;

    /**
     * Meta key for per-item shipped quantity
     */
    const META_QTY_SHIPPED = '_qty_shipped';

    /**
     * Calculate fulfillment status for a WooCommerce order
     *
     * @param WC_Order $order Order object.
     * @return string One of the STATUS_* constants.
     */
    public static function calculate($order) {
        $total_qty_ordered = 0;
        $total_qty_shipped = 0;

        foreach ($order->get_items() as $item_id => $item) {
            $product = $item->get_product();

            // Skip virtual/downloadable items
            if ($product && ($product->is_virtual() || $product->is_downloadable())) {
                continue;
            }

            $qty_ordered = (float) $item->get_quantity();
            $qty_shipped = (float) self::get_shipped_quantity($item);

            $total_qty_ordered += $qty_ordered;
            $total_qty_shipped += $qty_shipped;
        }

        // Determine status using epsilon for floating-point comparison
        if ($total_qty_shipped < self::FLOAT_EPSILON) {
            return self::STATUS_UNFULFILLED;
        }

        if ($total_qty_shipped >= ($total_qty_ordered - self::FLOAT_EPSILON)) {
            return self::STATUS_FULFILLED;
        }

        return self::STATUS_PARTIALLY_FULFILLED;
    }

    /**
     * Get the fulfillable quantity for an order item (remaining to ship)
     *
     * @param WC_Order_Item_Product $item Order item.
     * @return int Quantity remaining to fulfill.
     */
    public static function get_fulfillable_quantity($item) {
        $qty_ordered = (int) $item->get_quantity();
        $qty_shipped = (int) self::get_shipped_quantity($item);

        return max(0, $qty_ordered - $qty_shipped);
    }

    /**
     * Get shipped quantity for an order item from meta
     *
     * @param WC_Order_Item_Product $item Order item.
     * @return float Shipped quantity (defaults to 0).
     */
    public static function get_shipped_quantity($item) {
        $qty_shipped = wc_get_order_item_meta($item->get_id(), self::META_QTY_SHIPPED, true);
        return !empty($qty_shipped) ? (float) $qty_shipped : 0.0;
    }

    /**
     * Update shipped quantity for an order item
     *
     * @param int   $item_id      Order item ID.
     * @param float $qty_shipped  Quantity shipped.
     */
    public static function update_shipped_quantity($item_id, $qty_shipped) {
        wc_update_order_item_meta($item_id, self::META_QTY_SHIPPED, $qty_shipped);
    }
}
