<?php

/**
 * Checkout Migration Trait
 *
 * Handles checkout-time plan switching / migration for existing subscribers,
 * including pricing adjustments, proration, order metadata, and processing.
 *
 * @package ArraySubs\Features\SubscriptionCheckout\Services\Traits
 */

namespace ArraySubs\Features\SubscriptionCheckout\Services\Traits;

defined('ABSPATH') || exit;

use ArraySubs\Features\PlanSwitching\Services\PlanManager;
use ArraySubs\Features\PlanSwitching\Services\ProrationCalculator;

/**
 * Checkout migration methods
 */
trait CheckoutMigrationTrait
{
    /**
     * Apply checkout migration pricing to an eligible cart item.
     *
     * @param \WC_Cart $cart Cart object.
     * @return void
     */
    public function applyCheckoutMigrationPricing($cart): void
    {
        if (is_admin() && !defined('DOING_AJAX')) {
            return;
        }

        if (!$cart || !method_exists($cart, 'get_cart')) {
            return;
        }

        $summary = $this->getSubscriptionCartSummary($cart);

        foreach ($cart->get_cart() as $cart_item_key => $cart_item) {
            $check_id = $this->getCartItemProductId($cart_item);

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

            $subscription_data = arraysubs_get_product_subscription_data($check_id);
            if (is_array($subscription_data) && isset($cart_item['data']) && method_exists($cart_item['data'], 'set_price')) {
                // Preserve the $0 trial price set by beforeCalculateTotals (priority 10).
                $has_trial = !empty($subscription_data['trial_length']) && (int) $subscription_data['trial_length'] > 0;

                if ($has_trial) {
                    $price = 0;
                } else {
                    $quantity = max(1, absint($cart_item['quantity'] ?? 1));
                    $gateway_context = function_exists('arraysubs_get_current_renewal_sync_gateway_context')
                        ? arraysubs_get_current_renewal_sync_gateway_context()
                        : ['id' => '', 'gateway' => null];
                    $sync_context = function_exists('arraysubs_get_renewal_sync_context')
                        ? arraysubs_get_renewal_sync_context($subscription_data, $quantity, '', (string) $gateway_context['id'], $gateway_context['gateway'])
                        : ['applies' => false];
                    $price = !empty($sync_context['applies'])
                        ? (float) $sync_context['initial_unit_amount']
                        : (float) ($subscription_data['price'] ?? $cart_item['data']->get_price());
                }

                $cart_item['data']->set_price($price);
            }

            unset($cart->cart_contents[$cart_item_key]['arraysubs_checkout_migration']);
        }

        if (!function_exists('arraysubs_can_auto_migrate_subscription_on_checkout') || !arraysubs_can_auto_migrate_subscription_on_checkout()) {
            return;
        }

        if (($summary['subscription_item_count'] ?? 0) !== 1 || ($summary['distinct_subscription_count'] ?? 0) !== 1) {
            return;
        }

        $identity = $this->getCheckoutIdentity();
        if (($identity['customer_id'] ?? 0) <= 0 && empty($identity['email'])) {
            return;
        }

        $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);

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

        foreach ($cart->get_cart() as $cart_item_key => $cart_item) {
            if ($this->getCartItemProductId($cart_item) !== $target_product_id) {
                continue;
            }

            $cart->cart_contents[$cart_item_key]['arraysubs_checkout_migration'] = $migration_context;

            if (isset($cart_item['data']) && method_exists($cart_item['data'], 'set_price')) {
                $cart_item['data']->set_price((float) $migration_context['display_price']);
            }
        }
    }

    /**
     * Append checkout migration details to subscription cart metadata.
     *
     * @param array $details   Existing metadata rows.
     * @param array $cart_item Cart item data.
     * @param array $data      Subscription data.
     * @param int   $quantity  Cart item quantity.
     * @return array
     */
    public function appendCheckoutMigrationCartItemData(array $details, array $cart_item, array $data, int $quantity): array
    {
        $migration = $cart_item['arraysubs_checkout_migration'] ?? null;

        if (!is_array($migration) || empty($migration['eligible'])) {
            return $details;
        }

        $details[] = [
            'key'       => __('Checkout action', 'arraysubs'),
            'value'     => sprintf(
                /* translators: %s: current product name */
                __('Replaces your current %s subscription', 'arraysubs'),
                esc_html($migration['current_product_name'])
            ),
            'className' => 'arraysubs-subscription-meta arraysubs-subscription-meta--migration-action',
        ];

        $details[] = [
            'key'       => __('Due today', 'arraysubs'),
            'value'     => wp_kses_post($migration['proration']['formatted']['net'] ?? wc_price($migration['proration']['net_amount'] ?? 0)),
            'className' => 'arraysubs-subscription-meta arraysubs-subscription-meta--migration-total',
        ];

        if (!empty($migration['proration']['credit_amount'])) {
            $details[] = [
                'key'       => __('Credit applied', 'arraysubs'),
                'value'     => wp_kses_post($migration['proration']['formatted']['credit'] ?? wc_price($migration['proration']['credit_amount'])),
                'className' => 'arraysubs-subscription-meta arraysubs-subscription-meta--migration-credit',
            ];
        }

        return $details;
    }

    /**
     * Store checkout migration metadata on the order and line item.
     *
     * @param \WC_Order_Item $item           Order line item.
     * @param string         $cart_item_key  Cart item key.
     * @param array          $values         Cart item values.
     * @param \WC_Order      $order          Order object.
     * @return void
     */
    public function addCheckoutMigrationOrderItemMeta($item, string $cart_item_key, array $values, $order): void
    {
        $migration = $values['arraysubs_checkout_migration'] ?? null;

        if (!is_array($migration) || empty($migration['eligible'])) {
            return;
        }

        $item->add_meta_data('_arraysubs_checkout_migration', 'yes', true);
        $item->add_meta_data('_arraysubs_subscription_id', absint($migration['subscription_id']), true);
        $item->add_meta_data('_arraysubs_new_product_id', absint($migration['new_product_id']), true);

        $order->update_meta_data('_arraysubs_order_type', 'plan_switch_checkout');
        $order->update_meta_data('_arraysubs_subscription_id', absint($migration['subscription_id']));
        $order->update_meta_data('_arraysubs_new_product_id', absint($migration['new_product_id']));
        $order->update_meta_data('_arraysubs_switch_quantity', absint($values['quantity'] ?? 1));
        $order->update_meta_data('_arraysubs_switch_type', sanitize_text_field($migration['switch_type']));
        $order->update_meta_data('_arraysubs_proration_data', $migration['proration']);
    }

    /**
     * Process a paid checkout migration order and update the existing subscription.
     *
     * @param int       $order_id Order ID.
     * @param \WC_Order $order   Order object.
     * @return void
     */
    public function maybeProcessCheckoutMigrationOrder(int $order_id, $order): void
    {
        if (!$order || $order->get_meta('_arraysubs_order_type') !== 'plan_switch_checkout') {
            return;
        }

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

        $subscription_id = absint($order->get_meta('_arraysubs_subscription_id'));
        $new_product_id = absint($order->get_meta('_arraysubs_new_product_id'));
        $quantity = absint($order->get_meta('_arraysubs_switch_quantity')) ?: 1;
        $proration = $order->get_meta('_arraysubs_proration_data');

        if ($subscription_id <= 0 || $new_product_id <= 0 || !is_array($proration)) {
            $order->add_order_note(__('Checkout migration could not be completed because required switch data was missing.', 'arraysubs'));
            return;
        }

        $result = PlanManager::updateSubscriptionProduct($subscription_id, $new_product_id, $proration, $quantity);

        if (is_wp_error($result)) {
            $order->add_order_note(sprintf(
                /* translators: %s: error message */
                __('Checkout migration failed: %s', 'arraysubs'),
                $result->get_error_message()
            ));
            return;
        }

        $this->linkMigratedOrderToSubscription($order, $subscription_id);

        $order->update_meta_data('_arraysubs_switch_processed', 'yes');
        $order->save();
        $order->add_order_note(__('Existing subscription updated from checkout migration.', 'arraysubs'));

        if (function_exists('arraysubs_add_system_note')) {
            arraysubs_add_system_note(
                $subscription_id,
                __('Existing subscription updated from checkout migration', 'arraysubs'),
                'private',
                'subscription',
                'checkout_migration'
            );
        }
    }

    /**
     * Build migration context for a checkout-time subscription replacement.
     *
     * @param int   $target_product_id Target product or variation ID.
     * @param int   $quantity          Requested quantity.
     * @param array $identity          Checkout identity.
     * @return array<string,mixed>
     */
    private function getCheckoutMigrationContext(int $target_product_id, int $quantity, array $identity): array
    {
        $context = [
            'eligible' => false,
            'message'  => __('One Subscription per Customer is enabled, so this order must either replace the customer\'s existing live subscription or be handled from the subscription screen.', 'arraysubs'),
        ];

        if ($target_product_id <= 0) {
            return $context;
        }

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

        if (empty($existing_subscriptions)) {
            $context['message'] = __('No live subscription was found for this customer, so One Subscription per Customer does not need a checkout migration for this order.', 'arraysubs');
            return $context;
        }

        if (count($existing_subscriptions) > 1) {
            $context['message'] = __('One Subscription per Customer is enabled, but this customer has more than one live subscription. Checkout cannot decide which subscription to replace; manage the correct subscription from the subscription screen first.', 'arraysubs');
            return $context;
        }

        if (!arraysubs_get_setting('plan_switching.enabled', true)) {
            $context['message'] = __('One Subscription per Customer is enabled and this customer already has a live subscription. Checkout migration requires Plan Switching to be enabled; turn on Plan Switching or manage the current subscription manually.', 'arraysubs');
            return $context;
        }

        $subscription_id = (int) reset($existing_subscriptions);
        $current_product_id = arraysubs_get_subscription_effective_product_id($subscription_id);

        if ($current_product_id <= 0) {
            $context['message'] = __('One Subscription per Customer is enabled, but the customer\'s current subscription could not be matched to a product. Manage the existing subscription manually before starting a new one.', 'arraysubs');
            return $context;
        }

        if ($current_product_id === $target_product_id) {
            $context['message'] = __('This customer already has this subscription plan. Because One Subscription per Customer is enabled, checkout can only continue when switching to a different allowed subscription product.', 'arraysubs');
            return $context;
        }

        $switch_type = ProrationCalculator::determineSwitchType($current_product_id, $target_product_id);

        if (!$this->isSwitchTypeEnabled($switch_type)) {
            $context['message'] = sprintf(
                /* translators: %s: switch type label */
                __('One Subscription per Customer is enabled and this checkout would need a %s, but that switch type is disabled in Plan Switching settings. Enable that switch type or manage the current subscription manually.', 'arraysubs'),
                strtolower($this->getSwitchTypeLabel($switch_type))
            );
            return $context;
        }

        if (!$this->isTargetAllowedForSubscriptionSwitch($current_product_id, $target_product_id, $switch_type)) {
            $context['message'] = __('One Subscription per Customer is enabled and this customer already has a live subscription. This product can only be purchased as a plan switch if it is configured as an allowed upgrade, downgrade, or crossgrade for the current subscription. Choose an allowed switch target or manage the existing subscription first.', 'arraysubs');
            return $context;
        }

        // Checkout replacement is an immediate paid flow. The global
        // Apply at Renewal mode belongs to explicit switch requests, not
        // cart replacement, otherwise the customer could receive an
        // immediate entitlement change from a zero-cost checkout.
        $proration_type = ProrationCalculator::PRORATE_IMMEDIATELY;
        $proration = ProrationCalculator::calculate($subscription_id, $target_product_id, $proration_type);

        if (empty($proration['success'])) {
            $context['message'] = $proration['error'] ?? __('One Subscription per Customer is enabled, but ArraySubs could not calculate the checkout migration price for this subscription. Manage the existing subscription manually before starting a new one.', 'arraysubs');
            return $context;
        }

        if ($quantity > 1) {
            $proration = ProrationCalculator::applyQuantity($proration, $quantity);
        }

        $current_product = wc_get_product($current_product_id);

        $context['eligible'] = true;
        $context['subscription_id'] = $subscription_id;
        $context['current_product_id'] = $current_product_id;
        $context['new_product_id'] = $target_product_id;
        $context['switch_type'] = $switch_type;
        $context['switch_type_label'] = $this->getSwitchTypeLabel($switch_type);
        $context['current_product_name'] = $current_product ? arraysubs_get_subscription_product_name($current_product) : __('Current subscription', 'arraysubs');
        $context['proration'] = $proration;
        $context['display_price'] = $quantity > 0 ? ((float) $proration['net_amount'] / $quantity) : (float) $proration['net_amount'];

        return $context;
    }

    /**
     * Check whether a switch type is enabled globally.
     *
     * @param string $switch_type Switch type.
     * @return bool
     */
    private function isSwitchTypeEnabled(string $switch_type): bool
    {
        $map = [
            'upgrade' => 'plan_switching.allow_upgrades',
            'downgrade' => 'plan_switching.allow_downgrades',
            'crossgrade' => 'plan_switching.allow_crossgrades',
        ];

        $setting_key = $map[$switch_type] ?? '';

        return !empty($setting_key) ? (bool) arraysubs_get_setting($setting_key, true) : false;
    }

    /**
     * Check whether a target product is configured as an allowed switch target.
     *
     * @param int    $current_product_id Current product ID.
     * @param int    $target_product_id  Target product ID.
     * @param string $switch_type        Switch type.
     * @return bool
     */
    private function isTargetAllowedForSubscriptionSwitch(int $current_product_id, int $target_product_id, string $switch_type): bool
    {
        $meta_map = [
            'upgrade' => '_arraysubs_upgrade_products',
            'downgrade' => '_arraysubs_downgrade_products',
            'crossgrade' => '_arraysubs_crossgrade_products',
        ];

        $meta_key = $meta_map[$switch_type] ?? '';
        if (empty($meta_key)) {
            return false;
        }

        $allowed_targets = get_post_meta($current_product_id, $meta_key, true);
        $allowed_targets = is_array($allowed_targets) ? array_map('absint', $allowed_targets) : [];

        return in_array($target_product_id, $allowed_targets, true);
    }

    /**
     * Get a readable switch type label.
     *
     * @param string $switch_type Switch type.
     * @return string
     */
    private function getSwitchTypeLabel(string $switch_type): string
    {
        $labels = [
            'upgrade' => __('Upgrade', 'arraysubs'),
            'downgrade' => __('Downgrade', 'arraysubs'),
            'crossgrade' => __('Crossgrade', 'arraysubs'),
        ];

        return $labels[$switch_type] ?? __('Plan change', 'arraysubs');
    }

    /**
     * Link a migration order to the subscription it updated.
     *
     * @param \WC_Order $order           Order object.
     * @param int       $subscription_id Subscription ID.
     * @return void
     */
    private function linkMigratedOrderToSubscription($order, int $subscription_id): void
    {
        $order->update_meta_data('_subscription_ids', [$subscription_id]);

        foreach ($order->get_items() as $item_id => $item) {
            if ($item->get_meta('_arraysubs_checkout_migration', true) !== 'yes') {
                continue;
            }

            wc_add_order_item_meta($item_id, '_subscription_id', $subscription_id, true);
        }

        $order_ids_json = get_post_meta($subscription_id, '_order_ids', true);
        $order_ids = !empty($order_ids_json) ? json_decode($order_ids_json, true) : [];

        if (!is_array($order_ids)) {
            $order_ids = [];
        }

        if (!in_array($order->get_id(), $order_ids, true)) {
            $order_ids[] = $order->get_id();
            update_post_meta($subscription_id, '_order_ids', wp_json_encode($order_ids));
        }
    }
}
