<?php

/**
 * Plan Switch Controller
 *
 * REST API endpoints for subscription plan switching.
 *
 * @package ArraySubs\Features\PlanSwitching\REST
 * @since 1.0.0
 */

namespace ArraySubs\Features\PlanSwitching\REST;

defined('ABSPATH') || exit;

use ArraySubs\Supports\BaseRestController;
use ArraySubs\Features\PlanSwitching\Services\ProrationCalculator;
use ArraySubs\Features\PlanSwitching\Services\PlanManager;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * Plan switching REST controller
 */
class SwitchController extends BaseRestController
{
    /**
     * Enable auto-loading
     *
     * @var bool
     */
    public static $loadable = true;

    /**
     * REST namespace
     *
     * @var string
     */
    protected $namespace = 'arraysubs/v1';

    /**
     * Constructor
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Register REST routes
     */
    public function registerRoutes(): void
    {
        // Get switch options for a subscription
        register_rest_route($this->namespace, '/subscriptions/(?P<id>\d+)/switch-options', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'getSwitchOptions'],
                'permission_callback' => [$this, 'checkSubscriptionPermission'],
                'args' => [
                    'id' => [
                        'description' => __('Subscription ID', 'arraysubs'),
                        'type' => 'integer',
                        'required' => true,
                    ],
                ],
            ],
        ]);

        // Preview proration for a plan switch
        // Note: proration type is enforced server-side via the configured
        // `plan_switching.proration_type` setting; clients cannot override it.
        register_rest_route($this->namespace, '/subscriptions/(?P<id>\d+)/switch-preview', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'previewSwitch'],
                'permission_callback' => [$this, 'checkSubscriptionPermission'],
                'args' => [
                    'id' => [
                        'description' => __('Subscription ID', 'arraysubs'),
                        'type' => 'integer',
                        'required' => true,
                    ],
                    'new_product_id' => [
                        'description' => __('New product ID to switch to', 'arraysubs'),
                        'type' => 'integer',
                        'required' => true,
                    ],
                    'quantity' => [
                        'description' => __('New quantity', 'arraysubs'),
                        'type' => 'integer',
                        'default' => 1,
                    ],
                ],
            ],
        ]);

        // Execute a plan switch
        // Note: proration type is enforced server-side via the configured
        // `plan_switching.proration_type` setting; clients cannot override it.
        register_rest_route($this->namespace, '/subscriptions/(?P<id>\d+)/switch', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'executeSwitch'],
                'permission_callback' => [$this, 'checkSubscriptionPermission'],
                'args' => [
                    'id' => [
                        'description' => __('Subscription ID', 'arraysubs'),
                        'type' => 'integer',
                        'required' => true,
                    ],
                    'new_product_id' => [
                        'description' => __('New product ID to switch to', 'arraysubs'),
                        'type' => 'integer',
                        'required' => true,
                    ],
                    'quantity' => [
                        'description' => __('New quantity', 'arraysubs'),
                        'type' => 'integer',
                        'default' => 1,
                    ],
                    'payment_method' => [
                        'description' => __('Payment method for proration charge', 'arraysubs'),
                        'type' => 'string',
                        'default' => '',
                    ],
                    'replace_pending' => [
                        'description' => __('Explicitly replace an existing pending switch.', 'arraysubs'),
                        'type' => 'boolean',
                        'default' => false,
                    ],
                ],
            ],
        ]);

        // Cancel a pending (deferred) plan switch
        register_rest_route($this->namespace, '/subscriptions/(?P<id>\d+)/pending-switch/cancel', [
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'cancelPendingSwitch'],
                'permission_callback' => [$this, 'checkSubscriptionPermission'],
                'args' => [
                    'id' => [
                        'description' => __('Subscription ID', 'arraysubs'),
                        'type' => 'integer',
                        'required' => true,
                    ],
                ],
            ],
        ]);

        // Get switch history for a subscription
        register_rest_route($this->namespace, '/subscriptions/(?P<id>\d+)/switch-history', [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'getSwitchHistory'],
                'permission_callback' => [$this, 'checkSubscriptionPermission'],
                'args' => [
                    'id' => [
                        'description' => __('Subscription ID', 'arraysubs'),
                        'type' => 'integer',
                        'required' => true,
                    ],
                ],
            ],
        ]);

        // Admin: Get plan switch settings
        register_rest_route($this->namespace, '/settings/plan-switching', [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'getSettings'],
                'permission_callback' => [$this, 'checkAdminPermission'],
            ],
            [
                'methods' => WP_REST_Server::CREATABLE,
                'callback' => [$this, 'saveSettings'],
                'permission_callback' => [$this, 'checkAdminPermission'],
                'args' => [
                    'settings' => [
                        'description' => __('Plan switching settings', 'arraysubs'),
                        'type' => 'object',
                        'required' => true,
                    ],
                ],
            ],
        ]);

        // Calculate prorated refund for cancellation
        register_rest_route($this->namespace, '/subscriptions/(?P<id>\d+)/prorated-refund', [
            [
                'methods' => WP_REST_Server::READABLE,
                'callback' => [$this, 'getProratedRefund'],
                'permission_callback' => [$this, 'checkSubscriptionPermission'],
                'args' => [
                    'id' => [
                        'description' => __('Subscription ID', 'arraysubs'),
                        'type' => 'integer',
                        'required' => true,
                    ],
                ],
            ],
        ]);
    }

    /**
     * Check subscription permission
     *
     * @param WP_REST_Request $request Request object
     * @return bool
     */
    public function checkSubscriptionPermission(WP_REST_Request $request): bool
    {
        $subscription_id = absint($request->get_param('id'));

        // Admin can access all subscriptions
        if (current_user_can('manage_woocommerce') || current_user_can('manage_options')) {
            return true;
        }

        // Customer can only access their own subscription
        if (!is_user_logged_in()) {
            return false;
        }

        $subscription = arraysubs_get_subscription($subscription_id);
        if (!$subscription instanceof \WP_Post) {
            return false;
        }

        $customer_id = absint(get_post_meta($subscription_id, '_customer_id', true));

        if ($customer_id <= 0) {
            return false;
        }

        return $customer_id === get_current_user_id();
    }

    /**
     * Check admin permission
     *
     * @return bool
     */
    public function checkAdminPermission(): bool
    {
        return current_user_can('manage_woocommerce') || current_user_can('manage_options');
    }

    /**
     * Check whether the current request may initiate switch calculations/mutations.
     *
     * @param bool $enforce_customer_setting Whether to enforce customer switching for non-admin users.
     * @return WP_REST_Response|null
     */
    private function validateSwitchingAccess(bool $enforce_customer_setting = true): ?WP_REST_Response
    {
        if (!arraysubs_get_setting('plan_switching.enabled', true)) {
            return arraysubs_rest_response(
                [],
                403,
                __('Plan switching is disabled.', 'arraysubs')
            );
        }

        if (!$enforce_customer_setting || $this->checkAdminPermission()) {
            return null;
        }

        if (!function_exists('arraysubs_is_customer_switch_allowed') || !arraysubs_is_customer_switch_allowed()) {
            return arraysubs_rest_response(
                [],
                403,
                __('Customer plan switching is disabled.', 'arraysubs')
            );
        }

        return null;
    }

    /**
     * Verify the REST nonce for state-changing switch actions.
     *
     * @param WP_REST_Request $request Request object.
     * @return WP_REST_Response|null
     */
    private function verifyRestNonce(WP_REST_Request $request): ?WP_REST_Response
    {
        $nonce = (string) $request->get_header('X-WP-Nonce');
        if ($nonce === '') {
            $nonce = (string) $request->get_param('_wpnonce');
        }

        if ($nonce === '' || !wp_verify_nonce($nonce, 'wp_rest')) {
            return arraysubs_rest_response(
                [],
                403,
                __('Security check failed. Please refresh the page and try again.', 'arraysubs')
            );
        }

        return null;
    }

    /**
     * Resolve the proration type for a switch request.
     *
     * The proration type is sourced exclusively from the admin-configured
     * `plan_switching.proration_type` setting. Any value supplied in the REST
     * request is ignored so customers cannot bypass the configured policy by
     * crafting their own request.
     *
     * @param mixed $value Raw value from the REST request (ignored).
     * @return string
     */
    private function normalizeProrationType($value): string
    {
        unset($value);

        $allowed = [
            ProrationCalculator::PRORATE_IMMEDIATELY,
            ProrationCalculator::APPLY_AT_RENEWAL,
            ProrationCalculator::NO_PRORATION,
        ];

        $configured = (string) arraysubs_get_setting(
            'plan_switching.proration_type',
            ProrationCalculator::PRORATE_IMMEDIATELY
        );

        return in_array($configured, $allowed, true)
            ? $configured
            : ProrationCalculator::PRORATE_IMMEDIATELY;
    }

    /**
     * Check whether a switch type is currently allowed by settings.
     *
     * @param string $switch_type 'upgrade', 'downgrade' or 'crossgrade'.
     * @return bool
     */
    private function isSwitchTypeAllowed(string $switch_type): bool
    {
        $map = [
            'upgrade' => 'plan_switching.allow_upgrades',
            'downgrade' => 'plan_switching.allow_downgrades',
            'crossgrade' => 'plan_switching.allow_crossgrades',
        ];

        if (!isset($map[$switch_type])) {
            return false;
        }

        return (bool) arraysubs_get_setting($map[$switch_type], true);
    }

    /**
     * Get available switch options for a subscription
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response
     */
    public function getSwitchOptions(WP_REST_Request $request): WP_REST_Response
    {
        $subscription_id = absint($request->get_param('id'));

        $access_error = $this->validateSwitchingAccess(true);
        if ($access_error) {
            return $access_error;
        }

        // Check subscription status
        $subscription = arraysubs_get_subscription($subscription_id);
        if (!$subscription instanceof \WP_Post) {
            return arraysubs_rest_response(
                [],
                404,
                __('Subscription not found', 'arraysubs')
            );
        }

        $allowed_statuses = ['arraysubs-active', 'arraysubs-trial'];
        if (!in_array($subscription->post_status, $allowed_statuses, true)) {
            return arraysubs_rest_response(
                [],
                400,
                __('Plan switching is only available for active subscriptions', 'arraysubs')
            );
        }

        $options = ProrationCalculator::getAvailableSwitchOptions($subscription_id);

        $current_product_id = absint(
            get_post_meta($subscription_id, '_variation_id', true)
                ?: get_post_meta($subscription_id, '_product_id', true)
        );

        return arraysubs_rest_response(
            [
                'subscription_id' => $subscription_id,
                'current_product_id' => $current_product_id,
                'options' => $options,
                'pending_switch' => function_exists('arraysubs_get_pending_switch_details')
                    ? arraysubs_get_pending_switch_details($subscription_id)
                    : arraysubs_get_pending_switch($subscription_id),
                'settings' => [
                    'allow_upgrades' => (bool) arraysubs_get_setting('plan_switching.allow_upgrades', true),
                    'allow_downgrades' => (bool) arraysubs_get_setting('plan_switching.allow_downgrades', true),
                    'allow_crossgrades' => (bool) arraysubs_get_setting('plan_switching.allow_crossgrades', true),
                    'default_proration_type' => arraysubs_get_setting('plan_switching.proration_type', 'prorate_immediately'),
                ],
            ],
            200,
            __('Switch options retrieved', 'arraysubs')
        );
    }

    /**
     * Preview a plan switch
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response
     */
    public function previewSwitch(WP_REST_Request $request): WP_REST_Response
    {
        $subscription_id = absint($request->get_param('id'));
        $new_product_id = absint($request->get_param('new_product_id'));
        // Proration type is sourced from settings, not the request payload.
        $proration_type = $this->normalizeProrationType(null);
        $quantity = absint($request->get_param('quantity')) ?: 1;

        $access_error = $this->validateSwitchingAccess(true);
        if ($access_error) {
            return $access_error;
        }

        // Validate product
        $new_product = wc_get_product($new_product_id);
        if (!$new_product) {
            return arraysubs_rest_response(
                [],
                404,
                __('Target product not found', 'arraysubs')
            );
        }

        // Calculate proration
        $proration = ProrationCalculator::calculate($subscription_id, $new_product_id, $proration_type);

        if (!empty($proration['success']) && !$this->isSwitchTypeAllowed($proration['switch_type'] ?? '')) {
            return arraysubs_rest_response(
                [],
                403,
                __('This switch type is currently disabled.', 'arraysubs')
            );
        }

        if (!$proration['success']) {
            return arraysubs_rest_response(
                [],
                400,
                $proration['error'] ?? __('Failed to calculate proration', 'arraysubs')
            );
        }

        $proration = ProrationCalculator::applyQuantity($proration, $quantity);

        return arraysubs_rest_response(
            $proration,
            200,
            __('Proration preview calculated', 'arraysubs')
        );
    }

    /**
     * Execute a plan switch
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response
     */
    public function executeSwitch(WP_REST_Request $request): WP_REST_Response
    {
        $subscription_id = absint($request->get_param('id'));
        $new_product_id = absint($request->get_param('new_product_id'));
        // Proration type is sourced from settings, not the request payload.
        $proration_type = $this->normalizeProrationType(null);
        $quantity = absint($request->get_param('quantity')) ?: 1;
        $payment_method = sanitize_text_field($request->get_param('payment_method'));
        $replace_pending = (bool) $request->get_param('replace_pending');

        $nonce_error = $this->verifyRestNonce($request);
        if ($nonce_error) {
            return $nonce_error;
        }

        $access_error = $this->validateSwitchingAccess(true);
        if ($access_error) {
            return $access_error;
        }

        // Get subscription
        $subscription = arraysubs_get_subscription($subscription_id);
        if (!$subscription instanceof \WP_Post) {
            return arraysubs_rest_response(
                [],
                404,
                __('Subscription not found', 'arraysubs')
            );
        }

        if (
            !function_exists('arraysubs_acquire_subscription_mutation_lock')
            || !arraysubs_acquire_subscription_mutation_lock($subscription_id)
        ) {
            return arraysubs_rest_response(
                [],
                409,
                __('This subscription is already being updated. Please refresh and try again.', 'arraysubs')
            );
        }

        try {
            $subscription = arraysubs_get_subscription($subscription_id);
            if (!$subscription instanceof \WP_Post) {
                return arraysubs_rest_response(
                    [],
                    404,
                    __('Subscription not found', 'arraysubs')
                );
            }

            // Check status
            $allowed_statuses = ['arraysubs-active', 'arraysubs-trial'];
            if (!in_array($subscription->post_status, $allowed_statuses, true)) {
                return arraysubs_rest_response(
                    [],
                    400,
                    __('Plan switching is only available for active subscriptions', 'arraysubs')
                );
            }

            // Block all switching while a cancellation is pending. The renewal
            // that would apply a deferred switch is being cancelled, and an
            // immediate switch would conflict with the scheduled-cancel state.
            if (arraysubs_is_waiting_cancellation($subscription_id)) {
                return arraysubs_rest_response(
                    [],
                    409,
                    __('This subscription is scheduled for cancellation. Undo the cancellation first to switch plans.', 'arraysubs')
                );
            }

            // Calculate proration
            $proration = ProrationCalculator::calculate($subscription_id, $new_product_id, $proration_type);

            if (!empty($proration['success']) && !$this->isSwitchTypeAllowed($proration['switch_type'] ?? '')) {
                return arraysubs_rest_response(
                    [],
                    403,
                    __('This switch type is currently disabled.', 'arraysubs')
                );
            }

            if (!$proration['success']) {
                return arraysubs_rest_response(
                    [],
                    400,
                    $proration['error'] ?? __('Failed to calculate proration', 'arraysubs')
                );
            }

            $actor = $this->checkAdminPermission()
                ? 'admin:' . get_current_user_id()
                : 'customer';

            // APPLY_AT_RENEWAL — defer the entire switch to the next renewal.
            // We do NOT touch the product, recurring amount, or next payment
            // date here. The renewal order will be built using the target plan
            // (see OrderCreation::createRenewalOrder), and on payment success
            // OrderIntegration::processRenewalPayment will call PlanManager
            // to flip the subscription. Cancellation undoes the pending switch.
            if ($proration_type === ProrationCalculator::APPLY_AT_RENEWAL) {
                $new_product = wc_get_product($new_product_id);
                if (!$new_product) {
                    return arraysubs_rest_response(
                        [],
                        404,
                        __('Target product not found', 'arraysubs')
                    );
                }

                $is_variation = $new_product->is_type('variation');
                $pending_proration = ProrationCalculator::applyQuantity($proration, $quantity);
                $existing_pending = function_exists('arraysubs_get_pending_switch_details')
                    ? arraysubs_get_pending_switch_details($subscription_id)
                    : arraysubs_get_pending_switch($subscription_id);

                if ($existing_pending !== null && !$replace_pending) {
                    return arraysubs_rest_response(
                        [
                            'subscription_id' => $subscription_id,
                            'pending_switch' => $existing_pending,
                            'requires_replace_confirmation' => true,
                        ],
                        409,
                        __('This subscription already has a pending plan switch. Confirm replacement to continue.', 'arraysubs')
                    );
                }

                $stored = arraysubs_set_pending_switch($subscription_id, [
                    'target_product_id'    => $is_variation ? $new_product->get_parent_id() : $new_product_id,
                    'target_variation_id'  => $is_variation ? $new_product_id : 0,
                    'quantity'             => $quantity,
                    'switch_type'          => $pending_proration['switch_type'] ?? '',
                    'switch_fee'           => isset($pending_proration['switch_fee']) ? (float) $pending_proration['switch_fee'] : 0,
                    'new_price'            => ProrationCalculator::resolveProductPrice($new_product),
                    'new_billing_period'   => $pending_proration['new_plan']['billing_period'] ?? '',
                    'new_billing_interval' => (int) ($pending_proration['new_plan']['billing_interval'] ?? 0),
                    'requested_by'         => $actor,
                ]);

                if (!$stored) {
                    return arraysubs_rest_response(
                        [],
                        500,
                        __('Failed to store the pending plan switch.', 'arraysubs')
                    );
                }

                if (function_exists('arraysubs_add_subscription_note')) {
                    if ($existing_pending !== null) {
                        $old_target = $existing_pending['target_product_name']
                            ?? __('the previous pending plan', 'arraysubs');
                        arraysubs_add_subscription_note(
                            $subscription_id,
                            sprintf(
                                /* translators: 1: old target plan, 2: new target plan */
                                __('Pending plan switch replaced: %1$s → %2$s.', 'arraysubs'),
                                $old_target,
                                arraysubs_get_subscription_product_name($new_product)
                            ),
                            'customer',
                            0,
                            [],
                            'subscription'
                        );
                    }

                    arraysubs_add_subscription_note(
                        $subscription_id,
                        sprintf(
                            /* translators: 1: switch type, 2: new product name, 3: effective date */
                            __('Plan %1$s to %2$s scheduled to take effect on the next renewal (%3$s).', 'arraysubs'),
                            ucfirst($proration['switch_type'] ?? 'switch'),
                            arraysubs_get_subscription_product_name($new_product),
                            get_post_meta($subscription_id, '_next_payment_date', true) ?: __('next payment date', 'arraysubs')
                        ),
                        'customer',
                        0,
                        [],
                        'subscription'
                    );
                }

                return arraysubs_rest_response(
                    [
                        'subscription_id' => $subscription_id,
                        'switch_type' => $pending_proration['switch_type'],
                        'deferred' => true,
                        'pending_switch' => function_exists('arraysubs_get_pending_switch_details')
                            ? arraysubs_get_pending_switch_details($subscription_id)
                            : arraysubs_get_pending_switch($subscription_id),
                    ],
                    200,
                    __('Plan switch scheduled for next renewal.', 'arraysubs')
                );
            }

            // Apply quantity
            $net_amount = $proration['net_amount'];
            if ($quantity > 1) {
                $net_amount *= $quantity;
            }

            // Store old product info before switch
            $old_product_id = get_post_meta($subscription_id, '_variation_id', true)
                ?: get_post_meta($subscription_id, '_product_id', true);

            // Create proration order if there's a net charge.
            //
            // PRORATE_IMMEDIATELY and NO_PRORATION both produce an immediate
            // charge order (the latter charges the full new plan price plus any
            // switch fee). APPLY_AT_RENEWAL defers the charge to the next
            // renewal invoice, so no order is created here.
            $order_id = null;
            $immediate_charge_modes = [
                ProrationCalculator::PRORATE_IMMEDIATELY,
                ProrationCalculator::NO_PRORATION,
            ];

            if ($net_amount > 0 && in_array($proration_type, $immediate_charge_modes, true)) {
                $order_id = PlanManager::createProrationOrder($subscription_id, $proration, $quantity, $payment_method);

                if (is_wp_error($order_id)) {
                    return arraysubs_rest_response(
                        [],
                        500,
                        $order_id->get_error_message()
                    );
                }

                // Check if order is paid (auto-payment might have succeeded)
                $order = wc_get_order($order_id);
                if (!$order || (!$order->is_paid() && !$order->has_status('completed'))) {
                    // Payment required, return checkout URL and stop here
                    return arraysubs_rest_response(
                        [
                            'subscription_id' => $subscription_id,
                            'proration_order_id' => $order_id,
                            'checkout_url' => $order ? $order->get_checkout_payment_url() : wc_get_checkout_url(),
                            'requires_payment' => true,
                            'switch_type' => $proration['switch_type'],
                            'old_product_id' => absint($old_product_id),
                            'new_product_id' => $new_product_id,
                        ],
                        200,
                        __('Proration order created. Please complete payment to finalize the plan switch.', 'arraysubs')
                    );
                }
                // Payment successful immediately, proceed to update logic below
            }

            // Update subscription with new product (only if no payment required or payment successful)
            $update_result = PlanManager::updateSubscriptionProduct($subscription_id, $new_product_id, $proration, $quantity);

            if (is_wp_error($update_result)) {
                // If order was created, cancel it
                if ($order_id) {
                    $order = wc_get_order($order_id);
                    if ($order) {
                        $order->update_status('cancelled', __('Plan switch failed', 'arraysubs'));
                    }
                }

                return arraysubs_rest_response(
                    [],
                    500,
                    $update_result->get_error_message()
                );
            }

            // Note: Actions like arraysubs_plan_switch_completed/add_subscription_note are now inside PlanManager::updateSubscriptionProduct

            $response_proration = ProrationCalculator::applyQuantity($proration, $quantity);

            return arraysubs_rest_response(
                [
                    'subscription_id' => $subscription_id,
                    'switch_type' => $proration['switch_type'],
                    'old_product_id' => absint($old_product_id),
                    'new_product_id' => $new_product_id,
                    'proration_order_id' => $order_id,
                    'next_payment_date' => $proration['new_next_payment_date'] ?? null,
                    'proration' => [
                        'credit' => $response_proration['credit_amount'],
                        'charge' => $response_proration['charge_amount'] ?? 0,
                        'net' => $response_proration['net_amount'],
                        'formatted' => $response_proration['formatted'],
                    ],
                ],
                200,
                sprintf(
                    /* translators: %s: switch type */
                    __('Plan %s completed successfully', 'arraysubs'),
                    $proration['switch_type']
                )
            );
        } finally {
            arraysubs_release_subscription_mutation_lock($subscription_id);
        }
    }

    /**
     * Cancel a pending (deferred / apply-at-renewal) plan switch.
     *
     * The customer or admin can undo the scheduled switch any time
     * before the next renewal fires. No fee is charged.
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response
     */
    public function cancelPendingSwitch(WP_REST_Request $request): WP_REST_Response
    {
        $subscription_id = absint($request->get_param('id'));

        $nonce_error = $this->verifyRestNonce($request);
        if ($nonce_error) {
            return $nonce_error;
        }

        $subscription = arraysubs_get_subscription($subscription_id);
        if (!$subscription instanceof \WP_Post) {
            return arraysubs_rest_response(
                [],
                404,
                __('Subscription not found', 'arraysubs')
            );
        }

        $pending = arraysubs_get_pending_switch($subscription_id);
        if ($pending === null) {
            return arraysubs_rest_response(
                [],
                404,
                __('No pending plan switch to cancel.', 'arraysubs')
            );
        }

        arraysubs_clear_pending_switch($subscription_id);

        if (function_exists('arraysubs_add_subscription_note')) {
            $actor_label = current_user_can('manage_woocommerce')
                ? __('admin', 'arraysubs')
                : __('customer', 'arraysubs');
            arraysubs_add_subscription_note(
                $subscription_id,
                sprintf(
                    /* translators: %s: actor (admin or customer) */
                    __('Pending plan switch cancelled by %s. The next renewal will continue with the current plan.', 'arraysubs'),
                    $actor_label
                ),
                'customer',
                0,
                [],
                'subscription'
            );
        }

        return arraysubs_rest_response(
            ['subscription_id' => $subscription_id],
            200,
            __('Pending plan switch cancelled.', 'arraysubs')
        );
    }

    /**
     * Get switch history for a subscription
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response
     */
    public function getSwitchHistory(WP_REST_Request $request): WP_REST_Response
    {
        $subscription_id = absint($request->get_param('id'));

        $subscription = arraysubs_get_subscription($subscription_id);
        if (!$subscription instanceof \WP_Post) {
            return arraysubs_rest_response(
                [],
                404,
                __('Subscription not found', 'arraysubs')
            );
        }

        $history = get_post_meta($subscription_id, '_plan_switch_history', true) ?: [];

        // Enrich with product names
        foreach ($history as &$entry) {
            $old_product = wc_get_product($entry['old_product_id']);
            $new_product = wc_get_product($entry['new_product_id']);

            $entry['old_product_name'] = $old_product ? arraysubs_get_subscription_product_name($old_product) : __('Unknown', 'arraysubs');
            $entry['new_product_name'] = $new_product ? arraysubs_get_subscription_product_name($new_product) : __('Unknown', 'arraysubs');
        }

        return arraysubs_rest_response(
            [
                'subscription_id' => $subscription_id,
                'history' => $history,
            ],
            200,
            __('Switch history retrieved', 'arraysubs')
        );
    }

    /**
     * Get prorated refund amount for cancellation
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response
     */
    public function getProratedRefund(WP_REST_Request $request): WP_REST_Response
    {
        $subscription_id = absint($request->get_param('id'));

        $refund = ProrationCalculator::calculateProrationRefund($subscription_id);

        if (!$refund['success']) {
            return arraysubs_rest_response(
                [],
                400,
                $refund['error'] ?? __('Failed to calculate refund', 'arraysubs')
            );
        }

        return arraysubs_rest_response(
            $refund,
            200,
            __('Prorated refund calculated', 'arraysubs')
        );
    }

    /**
     * Get plan switching settings
     *
     * @return WP_REST_Response
     */
    public function getSettings(): WP_REST_Response
    {
        $settings = [
            'enabled' => arraysubs_get_setting('plan_switching.enabled', true),
            'allow_upgrades' => arraysubs_get_setting('plan_switching.allow_upgrades', true),
            'allow_downgrades' => arraysubs_get_setting('plan_switching.allow_downgrades', true),
            'allow_crossgrades' => arraysubs_get_setting('plan_switching.allow_crossgrades', true),
            'proration_type' => arraysubs_get_setting('plan_switching.proration_type', 'prorate_immediately'),
            'auto_downgrade_timing' => arraysubs_get_setting('plan_switching.auto_downgrade_timing', 'on_expire'),
            'switch_fees' => arraysubs_get_setting('proration.switch_fees', [
                'upgrade' => 0,
                'downgrade' => 0,
                'crossgrade' => 0,
            ]),
            'rounding_method' => arraysubs_get_setting('proration.rounding_method', 'round'),
        ];

        return arraysubs_rest_response(
            $settings,
            200,
            __('Settings retrieved', 'arraysubs')
        );
    }

    /**
     * Save plan switching settings
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response
     */
    public function saveSettings(WP_REST_Request $request): WP_REST_Response
    {
        $settings = $request->get_param('settings');

        if (!is_array($settings)) {
            return arraysubs_rest_response(
                [],
                400,
                __('Invalid settings format', 'arraysubs')
            );
        }

        // Sanitize and save settings
        if (isset($settings['enabled'])) {
            arraysubs_update_setting('plan_switching.enabled', (bool) $settings['enabled']);
        }
        if (isset($settings['allow_upgrades'])) {
            arraysubs_update_setting('plan_switching.allow_upgrades', (bool) $settings['allow_upgrades']);
        }
        if (isset($settings['allow_downgrades'])) {
            arraysubs_update_setting('plan_switching.allow_downgrades', (bool) $settings['allow_downgrades']);
        }
        if (isset($settings['allow_crossgrades'])) {
            arraysubs_update_setting('plan_switching.allow_crossgrades', (bool) $settings['allow_crossgrades']);
        }
        if (isset($settings['proration_type'])) {
            $allowed_types = ['prorate_immediately', 'apply_at_renewal', 'no_proration'];
            $type = sanitize_text_field($settings['proration_type']);
            if (in_array($type, $allowed_types, true)) {
                arraysubs_update_setting('plan_switching.proration_type', $type);
            }
        }
        if (isset($settings['auto_downgrade_timing'])) {
            $allowed_timings = ['on_expire', 'on_cancel', 'on_trial_expire'];
            $timing = sanitize_text_field($settings['auto_downgrade_timing']);
            if (in_array($timing, $allowed_timings, true)) {
                arraysubs_update_setting('plan_switching.auto_downgrade_timing', $timing);
            }
        }
        if (isset($settings['switch_fees']) && is_array($settings['switch_fees'])) {
            $fees = [
                'upgrade' => floatval($settings['switch_fees']['upgrade'] ?? 0),
                'downgrade' => floatval($settings['switch_fees']['downgrade'] ?? 0),
                'crossgrade' => floatval($settings['switch_fees']['crossgrade'] ?? 0),
            ];
            arraysubs_update_setting('proration.switch_fees', $fees);
        }
        if (isset($settings['rounding_method'])) {
            $allowed_methods = ['round', 'round_up', 'round_down'];
            $method = sanitize_text_field($settings['rounding_method']);
            if (in_array($method, $allowed_methods, true)) {
                arraysubs_update_setting('proration.rounding_method', $method);
            }
        }

        return arraysubs_rest_response(
            ['saved' => true],
            200,
            __('Settings saved successfully', 'arraysubs')
        );
    }
}
