<?php

/**
 * Plan Manager Service
 *
 * Handles subscription plan updates and switch execution logic.
 *
 * @package ArraySubs\Features\PlanSwitching\Services
 * @since 1.0.0
 */

namespace ArraySubs\Features\PlanSwitching\Services;

use ArraySubs\Features\RecurringBilling\Services\RenewalScheduler;
use ArraySubs\Supports\ActionScheduler;

defined('ABSPATH') || exit;

/**
 * Plan Manager class
 */
class PlanManager
{
    /**
     * Create a WooCommerce order for proration charge
     *
     * @param int    $subscription_id Subscription ID
     * @param array  $proration       Proration details
     * @param int    $quantity        Quantity
     * @param string $payment_method  Payment method
     * @return int|\WP_Error Order ID or error
     */
    public static function createProrationOrder(int $subscription_id, array $proration, int $quantity, string $payment_method)
    {
        $subscription = arraysubs_get_subscription($subscription_id);
        if (!$subscription instanceof \WP_Post) {
            return new \WP_Error('invalid_subscription', __('Subscription not found', 'arraysubs'));
        }

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

        // Get new product
        $new_product = wc_get_product($proration['new_product_id']);
        if (!$new_product) {
            return new \WP_Error('invalid_product', __('New product not found', 'arraysubs'));
        }

        try {
            $order = wc_create_order([
                'customer_id' => absint($customer_id),
            ]);

            if (is_wp_error($order)) {
                return $order;
            }

            // Add line items for proration charge.
            //
            // The switch fee, when configured, is split out into its own
            // line item so the customer (and admin) can see how the total
            // breaks down between proration and the configured switch fee.
            $switch_fee = isset($proration['switch_fee']) ? (float) $proration['switch_fee'] : 0;
            $net_amount = (float) $proration['net_amount'];
            $proration_amount = max(0, $net_amount - $switch_fee);

            if ($quantity > 1) {
                $proration_amount *= $quantity;
                $switch_fee *= $quantity;
            }

            if ($proration_amount > 0 || $switch_fee <= 0) {
                $item = new \WC_Order_Item_Fee();
                $item->set_name(sprintf(
                    /* translators: 1: switch type, 2: new product name */
                    __('Plan %1$s to %2$s - Proration', 'arraysubs'),
                    ucfirst($proration['switch_type']),
                    arraysubs_get_subscription_product_name($new_product)
                ));
                $item->set_amount($proration_amount);
                $item->set_total($proration_amount);
                $order->add_item($item);
            }

            if ($switch_fee > 0) {
                $fee_item = new \WC_Order_Item_Fee();
                $fee_item->set_name(sprintf(
                    /* translators: %s: switch type label */
                    __('Plan %s switch fee', 'arraysubs'),
                    ucfirst($proration['switch_type'])
                ));
                $fee_item->set_amount($switch_fee);
                $fee_item->set_total($switch_fee);
                $order->add_item($fee_item);
            }

            // Set order meta
            $order->set_currency($proration['currency']);
            $order->update_meta_data('_subscription_id', $subscription_id); // Standard key for subscription-related order lookups
            $order->update_meta_data('_arraysubs_subscription_id', $subscription_id); // For plan switch specific lookups
            $order->update_meta_data('_arraysubs_order_type', 'plan_switch');
            $order->update_meta_data('_arraysubs_switch_type', $proration['switch_type']);
            $order->update_meta_data('_arraysubs_old_product_id', $proration['current_product_id']);
            $order->update_meta_data('_arraysubs_new_product_id', $proration['new_product_id']);
            $order->update_meta_data('_arraysubs_proration_data', $proration); // Store full proration data
            $order->update_meta_data('_arraysubs_switch_quantity', $quantity);

            // Copy billing address from subscription's original order
            $original_order_id = get_post_meta($subscription_id, '_original_order_id', true);

            if (!$original_order_id) {
                $original_order_id = get_post_meta($subscription_id, '_parent_order_id', true);
            }

            if ($original_order_id) {
                $original_order = wc_get_order($original_order_id);
                if ($original_order) {
                    $order->set_address($original_order->get_address('billing'), 'billing');
                    $order->set_address($original_order->get_address('shipping'), 'shipping');
                    $order->set_created_via('arraysubs_plan_switch');
                }
            }

            // Set payment method
            if (!empty($payment_method)) {
                $order->set_payment_method($payment_method);
            } else {
                // Use subscription's saved payment method
                $saved_method = get_post_meta($subscription_id, '_payment_method', true);
                if ($saved_method) {
                    $order->set_payment_method($saved_method);
                }
            }

            // Calculate totals
            $order->calculate_totals();

            // Add order note
            $order->add_order_note(sprintf(
                /* translators: 1: subscription ID, 2: switch type */
                __('Plan switch proration order for subscription #%1$d (%2$s)', 'arraysubs'),
                $subscription_id,
                $proration['switch_type']
            ));

            $order->save();

            // Keep proration payment on the standard manual WooCommerce flow
            self::attemptAutoPayment($order);

            return $order->get_id();
        } catch (\Exception $e) {
            return new \WP_Error('order_creation_failed', $e->getMessage());
        }
    }

    /**
     * Keep proration orders on manual payment flow.
     *
     * ArraySubs currently operates subscription transactions through manual
     * WooCommerce payment handling. Orders stay payable by the customer or admin.
     *
     * @param \WC_Order $order Order object
     */
    public static function attemptAutoPayment(\WC_Order $order): void
    {
        $order->add_order_note(
            __('Proration order awaiting manual payment from customer or admin.', 'arraysubs')
        );
    }

    /**
     * Update subscription with new product
     *
     * @param int   $subscription_id Subscription ID
     * @param int   $new_product_id  New product ID
     * @param array $proration       Proration details
     * @param int   $quantity        Quantity
     * @return bool|\WP_Error
     */
    public static function updateSubscriptionProduct(int $subscription_id, int $new_product_id, array $proration, int $quantity)
    {
        $new_product = wc_get_product($new_product_id);
        if (!$new_product) {
            return new \WP_Error('invalid_product', __('New product not found', 'arraysubs'));
        }

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

        // Determine if variation or simple product
        $is_variation = $new_product->is_type('variation');
        $parent_product_id = $is_variation ? $new_product->get_parent_id() : $new_product_id;

        // Update product reference
        update_post_meta($subscription_id, '_product_id', $parent_product_id);

        if ($is_variation) {
            update_post_meta($subscription_id, '_variation_id', $new_product_id);
        } else {
            delete_post_meta($subscription_id, '_variation_id');
        }

        // Update recurring amount as PER-UNIT price. Renewal-order code in
        // OrderCreation::createRenewalOrder() multiplies by `_quantity`
        // when building the order, so storing `price * quantity` here would
        // produce a quantity² charge on renewal.
        $new_price = ProrationCalculator::resolveProductPrice($new_product);
        update_post_meta($subscription_id, '_recurring_amount', $new_price);

        // Update quantity
        update_post_meta($subscription_id, '_quantity', $quantity);

        // Update billing period/interval if changed
        if (isset($proration['new_plan'])) {
            update_post_meta($subscription_id, '_billing_period', $proration['new_plan']['billing_period']);
            update_post_meta($subscription_id, '_billing_interval', $proration['new_plan']['billing_interval']);
        }

        // Update next payment date if provided
        if (!empty($proration['new_next_payment_date'])) {
            update_post_meta($subscription_id, '_next_payment_date', $proration['new_next_payment_date']);

            // Reschedule renewal if RenewalScheduler is available
            if (class_exists(RenewalScheduler::class)) {
                $next_payment_timestamp = strtotime($proration['new_next_payment_date']);
                if ($next_payment_timestamp) {
                    RenewalScheduler::unschedule($subscription_id);
                    RenewalScheduler::schedule($subscription_id, $next_payment_timestamp);
                }
            }
        }

        // Update subscription title
        $new_title = sprintf(
            /* translators: 1: product name, 2: subscription ID */
            __('%1$s - Subscription #%2$d', 'arraysubs'),
            arraysubs_get_subscription_product_name($new_product),
            $subscription_id
        );

        wp_update_post([
            'ID' => $subscription_id,
            'post_title' => $new_title,
        ]);

        // Create refund if downgrade with credit
        if (isset($proration['refund_amount']) && $proration['refund_amount'] > 0) {
            self::createDowngradeCredit($subscription_id, $proration['refund_amount'], $proration['currency']);
        }

        // Clear cancellation/pause data if any (effectively reactivating if pending cancellation)
        delete_post_meta($subscription_id, '_cancellation_reason');
        delete_post_meta($subscription_id, '_cancellation_reason_details');
        delete_post_meta($subscription_id, '_schedule_end');
        delete_post_meta($subscription_id, '_end_date');
        delete_post_meta($subscription_id, '_retention_offer_accepted'); // Clear retention state if switching plan

        // Unschedule any pending cancellations, expirations, or resume actions
        if (class_exists(ActionScheduler::class)) {
            ActionScheduler::unscheduleAll(ActionScheduler::HOOK_CANCEL_SUBSCRIPTION, [$subscription_id], ActionScheduler::GROUP_STATUS);
            ActionScheduler::unscheduleAll(ActionScheduler::HOOK_EXPIRE_SUBSCRIPTION, [$subscription_id], ActionScheduler::GROUP_STATUS);
            ActionScheduler::unscheduleAll(ActionScheduler::HOOK_RESUME_SUBSCRIPTION, [$subscription_id], ActionScheduler::GROUP_STATUS);
        }

        // Fire update action
        do_action('arraysubs_plan_switch_completed', $subscription_id, absint($old_product_id), $new_product_id, $proration['switch_type']);

        return true;
    }

    /**
     * Create credit for downgrade (store credit or refund)
     *
     * @param int    $subscription_id Subscription ID
     * @param float  $credit_amount   Credit amount
     * @param string $currency        Currency
     */
    public static function createDowngradeCredit(int $subscription_id, float $credit_amount, string $currency): void
    {
        // Store credit amount for future use
        $existing_credit = floatval(get_post_meta($subscription_id, '_store_credit', true));
        $new_credit = $existing_credit + $credit_amount;

        update_post_meta($subscription_id, '_store_credit', $new_credit);

        // Add note
        if (function_exists('arraysubs_add_subscription_note')) {
            arraysubs_add_subscription_note(
                $subscription_id,
                sprintf(
                    /* translators: %s: credit amount */
                    __('Store credit added from downgrade: %s', 'arraysubs'),
                    wc_price($credit_amount, ['currency' => $currency])
                ),
                'system',
                0,
                [],
                'subscription'
            );
        }

        // Fire action for potential refund processing
        do_action('arraysubs_downgrade_credit_created', $subscription_id, $credit_amount, $currency);
    }
}
