<?php

/**
 * Trial Checkout Trait
 *
 * Handles free-trial payment logic, zero-total order completion,
 * and trial-related cart checks.
 *
 * @package ArraySubs\Features\SubscriptionCheckout\Services\Traits
 */

namespace ArraySubs\Features\SubscriptionCheckout\Services\Traits;

defined('ABSPATH') || exit;

/**
 * Trial checkout methods
 */
trait TrialCheckoutTrait
{
    /**
     * Control whether the cart requires payment during a free-trial checkout.
     *
     * Two scenarios:
     *  1. require_payment_method OFF  → force needs_payment to FALSE so
     *     the order completes without a gateway (no charge, no card-on-file).
     *  2. require_payment_method ON   → force needs_payment to TRUE so
     *     the gateway UI is shown and a SetupIntent / card-on-file is captured
     *     even though the monetary total is $0.
     *
     * @param bool $needs_payment Whether the cart needs payment.
     * @return bool
     */
    public function maybeSkipPaymentForTrial(bool $needs_payment): bool
    {
        if (!$this->isTrialOnlyCart()) {
            return $needs_payment;
        }

        if (!function_exists('arraysubs_trial_requires_payment_method')) {
            return $needs_payment;
        }

        // Has signup fee — always require payment regardless of setting.
        if ($this->cartHasSignupFee()) {
            return true;
        }

        // Setting ON  → require payment (gateway collects card-on-file).
        // Setting OFF → skip payment entirely.
        return arraysubs_trial_requires_payment_method();
    }

    /**
     * Control whether the order requires payment during a free-trial checkout.
     *
     * Mirrors the cart-level filter for the WC_Order object so that WooCommerce
     * Store API / Block Checkout also handles trial payment correctly.
     *
     * @param bool      $needs_payment Whether the order needs payment.
     * @param \WC_Order $order         Order object.
     * @return bool
     */
    public function maybeSkipOrderPaymentForTrial(bool $needs_payment, $order): bool
    {
        if (!$this->isTrialOnlyCart()) {
            return $needs_payment;
        }

        if (!function_exists('arraysubs_trial_requires_payment_method')) {
            return $needs_payment;
        }

        if ($this->cartHasSignupFee()) {
            return true;
        }

        return arraysubs_trial_requires_payment_method();
    }

    /**
     * Force safe initial subscription checkout orders to complete after payment.
     *
     * WooCommerce sends paid orders to `processing` unless every item is both
     * virtual and downloadable. ArraySubs should only override that when the
     * subscription order has no physical shipping work left: zero-total signups
     * or subscription-only orders whose products do not need shipping.
     *
     * @param string    $status   Proposed next order status.
     * @param int       $order_id WooCommerce order ID.
     * @param \\WC_Order $order    Order object.
     * @return string
     */
    public function filterSubscriptionPaymentCompleteOrderStatus(string $status, int $order_id, $order): string
    {
        if (
            $this->isZeroTotalInitialSubscriptionOrder($order)
            || $this->isCompletableInitialSubscriptionOrder($order)
            || $this->isCompletableRenewalSubscriptionOrder($order)
        ) {
            return 'completed';
        }

        return $status;
    }

    /**
     * Auto-complete zero-total subscription orders on the order-received page.
     *
     * Some payment gateways capture a card-on-file (SetupIntent / trial mandate) for
     * $0 trial checkouts but do not call payment_complete() themselves because no money
     * changed hands.  When the customer lands on the order-received page the order is
     * still in pending status.
     *
     * This method fires on woocommerce_thankyou (the order-received page) and calls
     * payment_complete() for any pending, zero-total subscription checkout order so that
     * downstream hooks (arraysubs_order_paid → activateSubscriptionsFromOrder) fire and
     * put the subscription into its correct active / trial status.
     *
     * @param int $order_id WooCommerce order ID.
     * @return void
     */
    public function maybeAutoCompleteZeroTrialOrder(int $order_id): void
    {
        $order = wc_get_order($order_id);

        if (!$this->isZeroTotalInitialSubscriptionOrder($order)) {
            return;
        }

        if (!$order instanceof \WC_Order || $order->get_status() === 'completed') {
            return;
        }

        // Pending orders need the payment-complete lifecycle so paid hooks fire.
        if ($order->get_status() === 'pending') {
            $order->payment_complete();
            $order = wc_get_order($order_id);
        }

        if (!$order instanceof \WC_Order || $order->get_status() === 'completed') {
            return;
        }

        $order->update_status(
            'completed',
            __('Zero-total subscription checkout auto-completed by ArraySubs.', 'arraysubs')
        );
    }

    /**
     * Check whether the current cart contains ONLY trial subscription products.
     *
     * Returns true when every subscription product in the cart has a free trial.
     * Non-subscription products (mixed cart) are ignored for this check.
     *
     * @return bool
     */
    private function isTrialOnlyCart(): bool
    {
        if (!function_exists('WC') || !WC()->cart) {
            return false;
        }

        $cart = WC()->cart;
        $has_subscription = false;

        foreach ($cart->get_cart() as $cart_item) {
            $product_id   = $cart_item['product_id'];
            $variation_id = $cart_item['variation_id'] ?? 0;
            $check_id     = $variation_id > 0 ? $variation_id : $product_id;

            if (!arraysubs_is_subscription_product($check_id)) {
                continue;
            }

            $has_subscription = true;
            $data = arraysubs_get_product_subscription_data($check_id);

            if (!$data) {
                return false;
            }

            $has_trial = !empty($data['trial_length']) && (int) $data['trial_length'] > 0;

            if (!$has_trial) {
                return false;
            }
        }

        return $has_subscription;
    }

    /**
     * Check whether an order is a zero-total initial subscription checkout order.
     *
     * These are safe to auto-complete because they are not renewal orders and they
     * already have linked subscriptions created during checkout.
     *
     * @param \\WC_Order|mixed $order Order object.
     * @return bool
     */
    private function isZeroTotalInitialSubscriptionOrder($order): bool
    {
        if (!$order instanceof \WC_Order) {
            return false;
        }

        if ((float) $order->get_total() > 0) {
            return false;
        }

        if ($order->get_meta('_is_renewal_order') === 'yes') {
            return false;
        }

        $subscription_ids = get_post_meta($order->get_id(), '_subscription_ids', true);

        return !empty($subscription_ids) && is_array($subscription_ids);
    }

    /**
     * Check whether a paid initial subscription order can skip processing.
     *
     * @param \\WC_Order|mixed $order Order object.
     * @return bool
     */
    private function isCompletableInitialSubscriptionOrder($order): bool
    {
        if (!$order instanceof \WC_Order || $order->get_meta('_is_renewal_order') === 'yes') {
            return false;
        }

        if ((float) $order->get_total() <= 0) {
            return false;
        }

        $subscription_ids = function_exists('arraysubs_get_subscription_ids_for_order')
            ? arraysubs_get_subscription_ids_for_order($order)
            : get_post_meta($order->get_id(), '_subscription_ids', true);

        if (empty($subscription_ids) || !is_array($subscription_ids)) {
            return false;
        }

        return $this->orderContainsOnlyNonShippingSubscriptionProducts($order);
    }

    /**
     * Check whether a paid renewal order can skip processing.
     *
     * @param \\WC_Order|mixed $order Order object.
     * @return bool
     */
    private function isCompletableRenewalSubscriptionOrder($order): bool
    {
        if (!$order instanceof \WC_Order || $order->get_meta('_is_renewal_order') !== 'yes') {
            return false;
        }

        if ((float) $order->get_shipping_total() > 0 || !empty($order->get_items('shipping'))) {
            return false;
        }

        $subscription_id = absint($order->get_meta('_subscription_id', true));
        if ($subscription_id <= 0) {
            return false;
        }

        return $this->orderContainsOnlyNonShippingSubscriptionProducts($order);
    }

    /**
     * Check whether all order line items are subscription products that do not need shipping.
     *
     * @param \\WC_Order $order Order object.
     * @return bool
     */
    private function orderContainsOnlyNonShippingSubscriptionProducts(\WC_Order $order): bool
    {
        $has_subscription_item = false;

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

            if (!$product instanceof \WC_Product) {
                continue;
            }

            if ($product->needs_shipping()) {
                return false;
            }

            $product_id = $product->get_id();
            $parent_id  = $product->get_parent_id();

            if (!arraysubs_is_subscription_product($product_id) && ($parent_id <= 0 || !arraysubs_is_subscription_product($parent_id))) {
                return false;
            }

            $has_subscription_item = true;
        }

        return $has_subscription_item;
    }

    /**
     * Check whether the current cart has any signup fee on subscription products.
     *
     * @return bool
     */
    private function cartHasSignupFee(): bool
    {
        if (!function_exists('WC') || !WC()->cart) {
            return false;
        }

        foreach (WC()->cart->get_cart() as $cart_item) {
            $product_id   = $cart_item['product_id'];
            $variation_id = $cart_item['variation_id'] ?? 0;
            $check_id     = $variation_id > 0 ? $variation_id : $product_id;

            if (!arraysubs_is_subscription_product($check_id)) {
                continue;
            }

            $data = arraysubs_get_product_subscription_data($check_id);

            if ($data && !empty($data['signup_fee']) && (float) $data['signup_fee'] > 0) {
                return true;
            }
        }

        return false;
    }
}
