<?php

/**
 * Cart Validation Trait
 *
 * Handles add-to-cart validation, cart quantity updates, composition rules,
 * and checkout customer restriction checks.
 *
 * @package ArraySubs\Features\SubscriptionCheckout\Services\Traits
 */

namespace ArraySubs\Features\SubscriptionCheckout\Services\Traits;

defined('ABSPATH') || exit;

/**
 * Cart validation methods
 */
trait CartValidationTrait
{
    /**
     * Validate add to cart for subscription products
     * Enforces multiple subscription settings and trial restrictions
     *
     * @param bool  $passed     Current validation status
     * @param int   $product_id Product ID being added
     * @param int   $quantity   Quantity being added
     * @param int   $variation_id Variation ID (if applicable)
     * @param array $variations Variation attributes
     * @return bool Whether the product can be added
     */
    public function validateAddToCart(bool $passed, int $product_id, int $quantity, int $variation_id = 0, array $variations = []): bool
    {
        if (!$passed) {
            return $passed;
        }

        // Validate product ID
        if ($product_id <= 0) {
            return $passed;
        }

        // Check if helper functions exist
        if (!function_exists('arraysubs_is_subscription_product') || !function_exists('arraysubs_get_customer_subscriptions')) {
            return $passed;
        }

        // Check if the product being added is a subscription
        $check_id = $variation_id > 0 ? $variation_id : $product_id;
        $is_subscription = arraysubs_is_subscription_product($check_id);

        // Non-subscription product: check mixed-cart restriction if cart has subscriptions
        if (!$is_subscription) {
            // One-click checkout products replace the cart, skip composition checks
            if (
                function_exists('arraysubs_is_one_click_checkout_product')
                && arraysubs_is_one_click_checkout_product($check_id)
            ) {
                return $passed;
            }

            if (function_exists('WC') && WC()->cart && !empty(WC()->cart->get_cart())) {
                $allow_mixed_default = (bool) arraysubs_get_setting('multiple_subscriptions.allow_mixed_cart', true);
                $allow_mixed         = apply_filters('arraysubs_allow_mixed_cart', $allow_mixed_default);

                if (!$allow_mixed) {
                    foreach (WC()->cart->get_cart() as $cart_item) {
                        $cid = ($cart_item['variation_id'] ?? 0) > 0 ? $cart_item['variation_id'] : $cart_item['product_id'];
                        if (arraysubs_is_subscription_product($cid)) {
                            wc_add_notice(
                                __('This cart already contains a subscription. Remove the subscription first or enable mixed checkout before adding regular products to the same order.', 'arraysubs'),
                                'error'
                            );
                            return false;
                        }
                    }
                }
            }

            return $passed;
        }

        // Cart composition checks (apply to all users including guests)
        $passed = $this->validateCartComposition($passed, $check_id);
        if (!$passed) {
            return false;
        }

        $passed = $this->validateSingleProductQuantity($passed, $check_id, $quantity);
        if (!$passed) {
            return false;
        }

        $passed = $this->validateSingleTrialQuantity($passed, $check_id, $quantity);
        if (!$passed) {
            return false;
        }

        // One subscription per customer: prevent adding a second distinct subscription to the cart.
        // This closes the gap where a new customer (no DB subscriptions) could add multiple
        // subscription products and end up with more than one subscription after checkout.
        if (
            function_exists('arraysubs_is_one_subscription_per_customer')
            && arraysubs_is_one_subscription_per_customer()
            && function_exists('WC') && WC()->cart
            && !(function_exists('arraysubs_is_one_click_checkout_product') && arraysubs_is_one_click_checkout_product($check_id))
        ) {
            foreach (WC()->cart->get_cart() as $cart_item) {
                $item_check_id = $this->getCartItemProductId($cart_item);
                if (
                    $item_check_id > 0
                    && $item_check_id !== $check_id
                    && arraysubs_is_subscription_product($item_check_id)
                ) {
                    wc_add_notice(
                        __('This store allows only one subscription per customer. Remove the other subscription from your cart before adding this one.', 'arraysubs'),
                        'error'
                    );
                    return false;
                }
            }
        }

        $user_id = get_current_user_id();
        $identity = $this->getCheckoutIdentity([
            'customer_id' => $user_id,
        ]);

        // Guest users - skip subscription-per-customer checks (they can't have existing subscriptions)
        if ($user_id <= 0) {
            return $passed;
        }

        // Check: One subscription per customer
        if (function_exists('arraysubs_is_one_subscription_per_customer') && arraysubs_is_one_subscription_per_customer()) {
            $existing_subscriptions = arraysubs_get_customer_subscriptions_for_identity(
                $identity['customer_id'],
                $identity['email'],
                $this->getRestrictedSubscriptionStatuses()
            );

            if (!empty($existing_subscriptions)) {
                if (function_exists('arraysubs_can_auto_migrate_subscription_on_checkout') && arraysubs_can_auto_migrate_subscription_on_checkout()) {
                    $migration_context = $this->getCheckoutMigrationContext($check_id, $quantity, $identity);

                    if ($migration_context['eligible']) {
                        return $passed;
                    }

                    wc_add_notice($migration_context['message'], 'error');
                    return false;
                }

                wc_add_notice(
                    __('You already have a subscription on this account. This store allows only one subscription per customer, so please manage your current subscription before starting another one.', 'arraysubs'),
                    'error'
                );
                return false;
            }
        }

        // Check: One trial per customer
        if (function_exists('arraysubs_is_one_trial_per_customer') && arraysubs_is_one_trial_per_customer()) {
            if (function_exists('arraysubs_product_has_free_trial') && arraysubs_product_has_free_trial($check_id)) {
                // Check if customer has ever had a trial subscription (including expired/cancelled)
                $past_trials = arraysubs_get_customer_subscriptions($user_id, ['arraysubs-active', 'arraysubs-on-hold', 'arraysubs-trial', 'arraysubs-cancelled', 'arraysubs-expired']);
                foreach ($past_trials as $sub_id) {
                    $had_trial = (int) get_post_meta($sub_id, '_trial_length', true) > 0;
                    if ($had_trial) {
                        wc_add_notice(
                            __('You have already used a free trial. Free trials are limited to one per customer.', 'arraysubs'),
                            'error'
                        );
                        return false;
                    }
                }
            }
        }

        return $passed;
    }

    /**
     * Validate cart quantity updates for subscription products.
     *
     * @param bool   $passed        Current validation status.
     * @param string $cart_item_key Cart item key being updated.
     * @param array  $values        Cart item values.
     * @param int    $quantity      Requested quantity.
     * @return bool
     */
    public function validateCartQuantityUpdate(bool $passed, string $cart_item_key, array $values, $quantity): bool
    {
        if (!$passed) {
            return $passed;
        }

        $quantity = absint($quantity);

        if ($quantity <= 0) {
            return $passed;
        }

        $check_id = $this->getCartItemProductId($values);

        if ($check_id <= 0 || !arraysubs_is_subscription_product($check_id)) {
            return $passed;
        }

        // One per product: block quantity increase above 1
        if (
            $quantity > 1
            && function_exists('arraysubs_is_one_subscription_per_product')
            && arraysubs_is_one_subscription_per_product()
        ) {
            wc_add_notice(
                __('This subscription can only be purchased once per order. Reduce the quantity to 1 before continuing.', 'arraysubs'),
                'error'
            );
            return false;
        }

        if (!$this->exceedsAllowedTrialCartQuantity($check_id, $quantity, $cart_item_key)) {
            return $passed;
        }

        wc_add_notice($this->getTrialCartValidationMessage(), 'error');

        return false;
    }

    /**
     * Validate checkout restrictions for guests and email-matched returning customers.
     *
     * @param array     $data   Posted checkout data.
     * @param \WP_Error $errors Checkout validation errors.
     * @return void
     */
    public function validateCheckoutCustomerRestrictions(array $data, \WP_Error $errors): void
    {
        foreach ($this->getTrialQuantityRestrictionErrors() as $code => $message) {
            $errors->add($code, $message);
        }

        $identity = $this->getCheckoutIdentity([
            'customer_id' => get_current_user_id(),
            'email'       => sanitize_email($data['billing_email'] ?? ''),
        ]);

        foreach ($this->getCustomerRestrictionErrors($identity) as $code => $message) {
            $errors->add($code, $message);
        }
    }

    /**
     * Validate Store API checkout restrictions for guests and email-matched returning customers.
     *
     * @param \WC_Order $order  Order object.
     * @param \WP_Error $errors Validation errors.
     * @return void
     */
    public function validateCheckoutCustomerRestrictionsStoreApi($order, \WP_Error $errors): void
    {
        foreach ($this->getTrialQuantityRestrictionErrors() as $code => $message) {
            $errors->add($code, $message);
        }

        $identity = $this->getCheckoutIdentity([
            'customer_id' => (int) $order->get_customer_id(),
            'email'       => sanitize_email($order->get_billing_email()),
        ]);

        foreach ($this->getCustomerRestrictionErrors($identity) as $code => $message) {
            $errors->add($code, $message);
        }
    }

    /**
     * Validate cart composition rules when adding a subscription product.
     *
     * Checks mixed-cart, multiple-subscriptions, and different-billing-cycle restrictions.
     *
     * @param bool $passed   Current validation status.
     * @param int  $check_id Product or variation ID being added.
     * @return bool
     */
    private function validateCartComposition(bool $passed, int $check_id): bool
    {
        // One-click checkout products replace the cart, skip composition checks
        if (
            function_exists('arraysubs_is_one_click_checkout_product')
            && arraysubs_is_one_click_checkout_product($check_id)
        ) {
            return $passed;
        }

        if (!function_exists('WC') || !WC()->cart) {
            return $passed;
        }

        $cart_contents = WC()->cart->get_cart();

        if (empty($cart_contents)) {
            return $passed;
        }

        $has_subscription_in_cart = false;
        $has_regular_in_cart      = false;
        $cart_cycles              = [];
        $cart_subscription_ids    = [];

        foreach ($cart_contents as $cart_item) {
            $item_product_id = $cart_item['product_id'];
            $item_variation  = $cart_item['variation_id'] ?? 0;
            $item_check_id   = $item_variation > 0 ? $item_variation : $item_product_id;

            if (arraysubs_is_subscription_product($item_check_id)) {
                $has_subscription_in_cart = true;
                $cart_subscription_ids[$item_check_id] = true;
                if (function_exists('arraysubs_get_product_subscription_data')) {
                    $sub_data = arraysubs_get_product_subscription_data($item_check_id);
                    if ($sub_data) {
                        $cart_cycles[$sub_data['period'] . ':' . $sub_data['interval']] = true;
                    }
                }
            } else {
                $has_regular_in_cart = true;
            }
        }

        // Mixed cart: subscription being added while cart has regular items
        if ($has_regular_in_cart) {
            $allow_mixed_default = (bool) arraysubs_get_setting('multiple_subscriptions.allow_mixed_cart', true);
            $allow_mixed         = apply_filters('arraysubs_allow_mixed_cart', $allow_mixed_default);

            if (!$allow_mixed) {
                wc_add_notice(
                    __('This cart already contains a regular product. Remove it first or enable mixed checkout before adding a subscription to the same order.', 'arraysubs'),
                    'error'
                );
                return false;
            }
        }

        // Multiple subscriptions: another subscription being added while cart already has one
        if ($has_subscription_in_cart) {
            $allow_multiple_default = (bool) arraysubs_get_setting('multiple_subscriptions.allow_multiple_in_cart', true);
            $allow_multiple         = apply_filters('arraysubs_allow_multiple_subscriptions', $allow_multiple_default);

            $cart_would_have_distinct_subscriptions = $cart_subscription_ids;
            $cart_would_have_distinct_subscriptions[$check_id] = true;

            if (!$allow_multiple && count($cart_would_have_distinct_subscriptions) > 1) {
                wc_add_notice(
                    __('Only one subscription plan can be checked out at a time. Remove the other subscription from your cart before adding this one.', 'arraysubs'),
                    'error'
                );
                return false;
            }

            // Different billing cycles
            if (!empty($cart_cycles) && function_exists('arraysubs_get_product_subscription_data')) {
                $new_sub_data = arraysubs_get_product_subscription_data($check_id);
                if ($new_sub_data) {
                    $new_cycle = $new_sub_data['period'] . ':' . $new_sub_data['interval'];
                    $cart_cycles[$new_cycle] = true;

                    if (count($cart_cycles) > 1) {
                        $allow_diff_default = (bool) arraysubs_get_setting('multiple_subscriptions.allow_different_cycles', true);
                        $allow_diff         = apply_filters('arraysubs_allow_different_billing_cycles', $allow_diff_default);

                        if (!$allow_diff) {
                            wc_add_notice(
                                __('These subscription plans use different billing cycles. Keep only plans with the same renewal schedule, or enable mixed billing cycles in settings.', 'arraysubs'),
                                'error'
                            );
                            return false;
                        }
                    }
                }
            }
        }

        return $passed;
    }

    /**
     * Enforce one subscription per product as a cart quantity rule.
     *
     * @param bool $passed   Current validation status.
     * @param int  $check_id Product or variation ID being added.
     * @param int  $quantity Quantity being added.
     * @return bool
     */
    private function validateSingleProductQuantity(bool $passed, int $check_id, int $quantity): bool
    {
        if (!function_exists('arraysubs_is_one_subscription_per_product') || !arraysubs_is_one_subscription_per_product()) {
            return $passed;
        }

        if (!function_exists('WC') || !WC()->cart) {
            return $passed;
        }

        $existing_quantity = 0;

        foreach (WC()->cart->get_cart() as $cart_item) {
            $item_check_id = $this->getCartItemProductId($cart_item);

            if ($item_check_id !== $check_id) {
                continue;
            }

            $existing_quantity += max(1, absint($cart_item['quantity'] ?? 1));
        }

        if (($existing_quantity + max(1, $quantity)) <= 1) {
            return $passed;
        }

        wc_add_notice(
            __('This subscription can only be purchased once per order. Reduce the quantity to 1 before continuing.', 'arraysubs'),
            'error'
        );

        return false;
    }

    /**
     * Enforce a single free-trial item in the cart when one-trial-per-customer is enabled.
     *
     * @param bool $passed   Current validation status.
     * @param int  $check_id Product or variation ID being added.
     * @param int  $quantity Quantity being added.
     * @return bool
     */
    private function validateSingleTrialQuantity(bool $passed, int $check_id, int $quantity): bool
    {
        if (!$this->exceedsAllowedTrialCartQuantity($check_id, $quantity)) {
            return $passed;
        }

        wc_add_notice($this->getTrialCartValidationMessage(), 'error');

        return false;
    }

    /**
     * Collect customer-specific restriction errors for the current cart.
     *
     * @param array $identity Resolved checkout identity.
     * @return array<string,string>
     */
    private function getCustomerRestrictionErrors(array $identity): array
    {
        if (!function_exists('WC') || !WC()->cart) {
            return [];
        }

        $summary = $this->getSubscriptionCartSummary(WC()->cart);
        if (($summary['subscription_item_count'] ?? 0) === 0) {
            return [];
        }

        if (!function_exists('arraysubs_is_one_subscription_per_customer') || !arraysubs_is_one_subscription_per_customer()) {
            return [];
        }

        $existing_subscriptions = arraysubs_get_customer_subscriptions_for_identity(
            $identity['customer_id'] ?? 0,
            $identity['email'] ?? '',
            $this->getRestrictedSubscriptionStatuses()
        );

        if (empty($existing_subscriptions)) {
            return [];
        }

        if (!function_exists('arraysubs_can_auto_migrate_subscription_on_checkout') || !arraysubs_can_auto_migrate_subscription_on_checkout()) {
            return [
                'arraysubs_one_per_customer' => __('This checkout email already has a live subscription. This store allows only one subscription per customer, so please manage the existing subscription before placing this order.', 'arraysubs'),
            ];
        }

        if (($summary['subscription_item_count'] ?? 0) !== 1 || ($summary['distinct_subscription_count'] ?? 0) !== 1) {
            return [
                'arraysubs_checkout_migration_cart' => __('Checkout migration can replace only one subscription at a time. Keep a single subscription plan in the cart, then try again.', 'arraysubs'),
            ];
        }

        $target_product_id = (int) ($summary['first_subscription_product_id'] ?? 0);
        $quantity = (int) ($summary['quantities'][$target_product_id] ?? 1);
        $migration_context = $this->getCheckoutMigrationContext($target_product_id, $quantity, $identity);

        return $migration_context['eligible']
            ? []
            : ['arraysubs_checkout_migration' => $migration_context['message']];
    }

    /**
     * Collect checkout restriction errors for trial-quantity abuse.
     *
     * @return array<string,string>
     */
    private function getTrialQuantityRestrictionErrors(): array
    {
        if (!function_exists('WC') || !WC()->cart) {
            return [];
        }

        if (!function_exists('arraysubs_is_one_trial_per_customer') || !arraysubs_is_one_trial_per_customer()) {
            return [];
        }

        $summary = $this->getSubscriptionCartSummary(WC()->cart);
        $trial_quantity = array_sum($summary['trial_quantities'] ?? []);

        if ($trial_quantity <= 1) {
            return [];
        }

        return [
            'arraysubs_trial_quantity' => $this->getTrialCartValidationMessage(),
        ];
    }
}
