<?php

/**
 * Subscription Creation Trait
 *
 * Handles creating subscriptions from checkout orders, including
 * classic checkout, block checkout, and fallback order creation.
 *
 * @package ArraySubs\Features\SubscriptionCheckout\Services\Traits
 */

namespace ArraySubs\Features\SubscriptionCheckout\Services\Traits;

defined('ABSPATH') || exit;

/**
 * Subscription creation methods
 */
trait SubscriptionCreationTrait
{
    /**
     * Create subscriptions from Block Checkout (Store API)
     *
     * @param \WC_Order $order Order object
     */
    public function createSubscriptionsFromOrderApi($order): void
    {
        if (!$order) {
            return;
        }

        $this->createSubscriptionsFromOrder($order->get_id(), [], $order);
    }

    /**
     * Fallback: Create subscriptions when new order is created
     * Only processes if subscriptions haven't been created yet
     *
     * @param int       $order_id Order ID
     * @param \WC_Order $order Order object
     */
    public function maybeCreateSubscriptionsFromNewOrder($order_id, $order = null): void
    {
        // Check if subscriptions have already been created for this order
        $existing_subscriptions = get_post_meta($order_id, '_subscription_ids', true);
        if (!empty($existing_subscriptions)) {
            return;
        }

        // Check if this is a renewal order - don't create new subscriptions for renewals
        if (!$order) {
            $order = wc_get_order($order_id);
        }

        if (!$order) {
            return;
        }

        if ($order->has_status('checkout-draft')) {
            return;
        }

        if ($order->get_meta('_arraysubs_order_type') === 'plan_switch_checkout') {
            return;
        }

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

        $this->createSubscriptionsFromOrder($order_id, [], $order);
    }

    /**
     * Create subscriptions from order
     *
     * @param int       $order_id Order ID
     * @param array     $posted_data Posted checkout data
     * @param \WC_Order $order Order object (may be null in some cases)
     */
    public function createSubscriptionsFromOrder($order_id, $posted_data = [], $order = null): void
    {
        // Ensure we have an order object
        if (!$order) {
            $order = wc_get_order($order_id);
        }

        if (!$order) {
            return;
        }

        // IMPORTANT: Check if subscriptions have already been created for this order.
        // This prevents duplicates and lets Store API checkout sync customer ownership after
        // WooCommerce creates an account from a guest draft order.
        $existing_subscriptions = get_post_meta($order_id, '_subscription_ids', true);
        if (!empty($existing_subscriptions) && is_array($existing_subscriptions)) {
            $this->syncSubscriptionsWithOrderCustomer($existing_subscriptions, $order);
            return;
        }

        if ($order->get_meta('_arraysubs_order_type') === 'plan_switch_checkout') {
            return;
        }

        $is_one_per_customer = function_exists('arraysubs_is_one_subscription_per_customer')
            && arraysubs_is_one_subscription_per_customer();

        $subscription_ids = [];

        $items = $order->get_items();

        foreach ($items as $item_id => $item) {
            // One per customer: stop after the first subscription is created
            if ($is_one_per_customer && !empty($subscription_ids)) {
                break;
            }

            $product_id = $item->get_product_id();
            $variation_id = $item->get_variation_id();
            $check_id = $variation_id > 0 ? $variation_id : $product_id;

            // Check if this is a subscription product
            $is_subscription = get_post_meta($check_id, '_is_subscription', true);

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

            // Get subscription data
            $subscription_data = arraysubs_get_product_subscription_data($check_id);

            if (!$subscription_data) {
                continue;
            }

            // Create subscription
            $subscription_id = $this->createSubscription($order, $item, $subscription_data, $check_id);

            if (!is_wp_error($subscription_id)) {
                $subscription_ids[] = $subscription_id;

                // Store subscription ID in order item meta
                wc_add_order_item_meta($item_id, '_subscription_id', $subscription_id, true);
            }
        }

        if (!empty($subscription_ids)) {
            // Store all subscription IDs in order meta
            update_post_meta($order_id, '_subscription_ids', $subscription_ids);
        }
    }

    /**
     * Create a single subscription
     *
     * @param \WC_Order      $order Order object
     * @param \WC_Order_Item $item Order item
     * @param array          $subscription_data Subscription product data
     * @param int            $product_id Product or variation ID
     * @return int|\WP_Error Subscription ID or error
     */
    private function createSubscription($order, $item, array $subscription_data, int $product_id)
    {
        $customer_id = $order->get_customer_id();
        $start_date = current_time('mysql', true);

        // Check if trial period exists
        $has_trial = !empty($subscription_data['trial_length']) && $subscription_data['trial_length'] > 0;
        $payment_method = (string) $order->get_payment_method();
        $renewal_sync_context = function_exists('arraysubs_get_renewal_sync_context')
            ? arraysubs_get_renewal_sync_context($subscription_data, max(1, absint($item->get_quantity())), $start_date, $payment_method)
            : ['applies' => false];

        // Calculate next payment date
        if ($has_trial) {
            // Next payment after trial
            $next_payment_date = arraysubs_calculate_next_payment_date(
                $start_date,
                $subscription_data['trial_length'],
                $subscription_data['trial_period']
            );
        } elseif (!empty($renewal_sync_context['applies']) && !empty($renewal_sync_context['next_payment_date'])) {
            $next_payment_date = (string) $renewal_sync_context['next_payment_date'];
        } else {
            // Regular next payment date
            $next_payment_date = arraysubs_calculate_next_payment_date(
                $start_date,
                $subscription_data['interval'],
                $subscription_data['period']
            );
        }

        // Determine pricing
        // IMPORTANT: These prices are captured at checkout time and will be locked for this subscription
        // Future product price changes (including sale price changes) will NOT affect this subscription
        //
        // Price structure:
        // - subscription_price: The original price at checkout (for reference)
        // - recurring_amount: The current recurring price (this may be changed later after X payments if different renewal price is enabled)
        // - different_renewal_price: The price that will apply after X payments (if enabled)
        // - different_renewal_price_after: After how many payments to switch to different_renewal_price
        //
        // The recurring_amount ALWAYS starts with the current price (sale or regular).
        // OrderCreation.php handles switching to different_renewal_price after the threshold is met.
        $current_price = $subscription_data['price']; // This is already sale price if on sale, or regular price
        $recurring_amount = $current_price; // Start with current price, NOT the different renewal price

        $subscription_quantity = max(1, absint($item->get_quantity()));

        if (
            $has_trial
            && function_exists('arraysubs_is_one_trial_per_customer')
            && arraysubs_is_one_trial_per_customer()
        ) {
            $subscription_quantity = 1;
        }

        if (
            function_exists('arraysubs_is_one_subscription_per_product')
            && arraysubs_is_one_subscription_per_product()
        ) {
            $subscription_quantity = 1;
        }

        // Add signup fee to first payment if exists
        $signup_fee = !empty($subscription_data['signup_fee']) ? (float) $subscription_data['signup_fee'] : 0;

        // Create subscription with locked-in prices
        $subscription_id = arraysubs_create_subscription([
            'customer_id'         => $customer_id ?: 0,
            'customer_email'      => $order->get_billing_email(),
            'product_id'          => $item->get_product_id(),
            'variation_id'        => $item->get_variation_id(),
            'quantity'            => $subscription_quantity,
            'billing_period'      => $subscription_data['period'],
            'billing_interval'    => $subscription_data['interval'],
            'subscription_length' => $subscription_data['length'],
            'subscription_price'  => $current_price,
            'recurring_amount'    => $recurring_amount,
            'trial_length'        => $has_trial ? $subscription_data['trial_length'] : 0,
            'trial_period'        => $has_trial ? $subscription_data['trial_period'] : '',
            'signup_fee'          => $signup_fee,
            'currency'            => $order->get_currency(),
            'start_date'          => $start_date,
            'next_payment_date'   => $next_payment_date,
            'status'              => $has_trial ? 'arraysubs-trial' : 'arraysubs-pending',
            'payment_method'      => $payment_method,
            'payment_method_title' => $order->get_payment_method_title(),
            'parent_order_id'     => $order->get_id(),
            'different_renewal_price' => $subscription_data['enable_different_renewal_price'] ? (float) $subscription_data['different_renewal_price'] : 0,
            'different_renewal_price_after' => $subscription_data['different_renewal_price_after'],
            'enable_different_renewal_price' => $subscription_data['enable_different_renewal_price'],
        ]);

        if (!is_wp_error($subscription_id)) {
            // Store addresses and invoice email for future renewal orders
            $this->storeSubscriptionAddresses($subscription_id, $order);

            if (!empty($renewal_sync_context['applies'])) {
                $renewal_sync_initial_amount = (float) ($renewal_sync_context['initial_line_amount'] ?? 0);

                if ($item instanceof \WC_Order_Item_Product) {
                    $renewal_sync_initial_amount = (float) $item->get_total();
                }

                update_post_meta($subscription_id, '_renewal_sync_enabled', 'yes');
                update_post_meta($subscription_id, '_renewal_sync_first_charge_mode', (string) $renewal_sync_context['mode']);
                update_post_meta($subscription_id, '_renewal_sync_cycle_start_date', (string) $renewal_sync_context['cycle_start_date']);
                update_post_meta($subscription_id, '_renewal_sync_first_full_renewal_date', (string) $renewal_sync_context['next_payment_date']);
                update_post_meta($subscription_id, '_renewal_sync_initial_recurring_amount', $renewal_sync_initial_amount);

                if ($item instanceof \WC_Order_Item_Product) {
                    $item->add_meta_data('_renewal_sync_enabled', 'yes', true);
                    $item->add_meta_data('_renewal_sync_first_charge_mode', (string) $renewal_sync_context['mode'], true);
                    $item->add_meta_data('_renewal_sync_cycle_start_date', (string) $renewal_sync_context['cycle_start_date'], true);
                    $item->add_meta_data('_renewal_sync_first_full_renewal_date', (string) $renewal_sync_context['next_payment_date'], true);
                    $item->add_meta_data('_renewal_sync_initial_recurring_amount', $renewal_sync_initial_amount, true);
                    $item->save();
                }
            }

            do_action('arraysubs_data_created_from_order', $subscription_id, $order->get_id(), $item);
        }

        return $subscription_id;
    }

    /**
     * Sync existing subscription ownership from the final checkout order customer.
     *
     * @param array     $subscription_ids Subscription IDs.
     * @param \WC_Order $order            Order object.
     * @return void
     */
    private function syncSubscriptionsWithOrderCustomer(array $subscription_ids, \WC_Order $order): void
    {
        $customer_id = absint($order->get_customer_id());
        if ($customer_id <= 0) {
            return;
        }

        $subscription_ids = array_values(array_unique(array_filter(array_map('absint', $subscription_ids))));
        if (empty($subscription_ids)) {
            return;
        }

        $customer = get_userdata($customer_id);
        $email    = $order->get_billing_email();
        if (empty($email) && $customer) {
            $email = $customer->user_email;
        }

        foreach ($subscription_ids as $subscription_id) {
            $subscription = get_post($subscription_id);
            if (! $subscription instanceof \WP_Post || $subscription->post_type !== 'arraysubs_data') {
                continue;
            }

            update_post_meta($subscription_id, '_customer_id', $customer_id);

            if (!empty($email)) {
                update_post_meta($subscription_id, '_customer_email', $email);
            }

            if ((int) $subscription->post_author !== $customer_id) {
                wp_update_post([
                    'ID'          => $subscription_id,
                    'post_author' => $customer_id,
                ]);
            }
        }
    }

    /**
     * Store billing and shipping addresses in subscription
     *
     * These addresses are used for all renewal orders and can be updated by admin or customer
     *
     * @param int       $subscription_id Subscription ID
     * @param \WC_Order $order Order object
     */
    private function storeSubscriptionAddresses(int $subscription_id, $order): void
    {
        // Store billing address
        $billing_address = [
            'first_name' => $order->get_billing_first_name(),
            'last_name'  => $order->get_billing_last_name(),
            'company'    => $order->get_billing_company(),
            'address_1'  => $order->get_billing_address_1(),
            'address_2'  => $order->get_billing_address_2(),
            'city'       => $order->get_billing_city(),
            'state'      => $order->get_billing_state(),
            'postcode'   => $order->get_billing_postcode(),
            'country'    => $order->get_billing_country(),
            'email'      => $order->get_billing_email(),
            'phone'      => $order->get_billing_phone(),
        ];

        update_post_meta($subscription_id, '_billing_address', wp_json_encode($billing_address));

        // Store shipping address (if different from billing)
        if ($order->has_shipping_address()) {
            $shipping_address = [
                'first_name' => $order->get_shipping_first_name(),
                'last_name'  => $order->get_shipping_last_name(),
                'company'    => $order->get_shipping_company(),
                'address_1'  => $order->get_shipping_address_1(),
                'address_2'  => $order->get_shipping_address_2(),
                'city'       => $order->get_shipping_city(),
                'state'      => $order->get_shipping_state(),
                'postcode'   => $order->get_shipping_postcode(),
                'country'    => $order->get_shipping_country(),
            ];

            update_post_meta($subscription_id, '_shipping_address', wp_json_encode($shipping_address));
        }

        // Store invoice email (defaults to billing email)
        update_post_meta($subscription_id, '_invoice_email', $order->get_billing_email());
    }
}
