<?php

namespace Licenzo\Modules\WooCommerce;

use Licenzo\Helper\Action;
use Licenzo\Helper\Settings;
use Licenzo\Helper\LicenzoKey;
use Licenzo\Model\Customer;
use Licenzo\Model\License;
use Licenzo\Model\LicenseProduct;
use Licenzo\Model\Product;
use Licenzo\Model\Subscription;
use Licenzo\Model\SubscriptionMeta;
use Licenzo\Model\SubscriptionProduct;
use Licenzo\Model\SubscriptionTransaction;
use Licenzo\Framework\Support\Facades\DB;
use Licenzo\Services\IdempotencyService;
use Licenzo\Services\Logging\SubscriptionEvent;

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

class WooOrderService
{
    /**
     * @var IdempotencyService
     */
    private $idempotencyService;

    public function __construct(?IdempotencyService $idempotencyService = null)
    {
        $this->idempotencyService = $idempotencyService ?? new IdempotencyService();
    }

    /**
     * Process a WooCommerce order and sync Licenzo entities.
     *
     * Two-phase lifecycle:
     *   Phase 1 (payment NOT confirmed): create pending Subscription + Transaction, no License.
     *   Phase 2 (payment confirmed): activate pending Subscription, create License, or
     *           fall through to direct creation for online gateways.
     *
     * @param int $orderId
     * @return void
     */
    public function process(int $orderId): void
    {
        if (!function_exists('wc_get_order')) {
            return;
        }

        $order = wc_get_order($orderId);
        if (!$order) {
            return;
        }

        $customer = $this->findOrCreateCustomer($order);
        if (!$customer) {
            return;
        }

        $paymentConfirmed = $this->isPaymentConfirmed($order);

        foreach ($order->get_items() as $item) {
            $productId = (int) $item->get_product_id();
            $variationId = (int) $item->get_variation_id();

            $product = $this->resolveProduct($productId, $variationId);
            if (!$product) {
                continue;
            }

            $variation = $variationId
                ? $product->variations->firstWhere('provider_id', (string) $variationId)
                : $product->variations->first();

            if (!$variation) {
                continue;
            }

            $linkedSub = $this->findLinkedSubscriptionForOrderItem($order, $item);

            if ($linkedSub && $linkedSub->status === Subscription::STATUS_PENDING && $paymentConfirmed) {
                $activationScope = 'woo_activate_pending';
                $activationKey = sprintf('%d:%d', $orderId, $linkedSub->id);
                $activationAcquired = $this->idempotencyService->acquire(
                    SubscriptionTransaction::PROVIDER_WOOCOMMERCE,
                    $activationScope,
                    $activationKey,
                    $linkedSub->id,
                    $customer->id,
                    hash('sha256', $activationKey),
                    gmdate('Y-m-d H:i:s', strtotime('+30 days'))
                );

                if (!$activationAcquired) {
                    continue;
                }

                try {
                    $this->activatePendingSubscription($linkedSub, $order, $item, $customer, $product, $variation);
                    $this->idempotencyService->markProcessed(SubscriptionTransaction::PROVIDER_WOOCOMMERCE, $activationScope, $activationKey);
                } catch (\Throwable $e) {
                    $this->idempotencyService->markFailed(SubscriptionTransaction::PROVIDER_WOOCOMMERCE, $activationScope, $activationKey);
                    $this->logOrderNote($order, sprintf(
                        'Licenzo failed activating pending subscription (order_id=%d, sub_id=%d): %s',
                        $orderId,
                        $linkedSub->id,
                        $e->getMessage()
                    ));
                }
                continue;
            }

            if ($linkedSub && $linkedSub->status === Subscription::STATUS_ACTIVE) {
                continue;
            }

            $eventScope = $paymentConfirmed ? 'woo_order_item' : 'woo_order_item_pending';
            $eventKey = $this->buildOrderItemEventKey($orderId, $item);
            $checksum = $this->buildOrderItemChecksum($orderId, $item);

            $acquired = $this->idempotencyService->acquire(
                SubscriptionTransaction::PROVIDER_WOOCOMMERCE,
                $eventScope,
                $eventKey,
                null,
                $customer->id,
                $checksum,
                gmdate('Y-m-d H:i:s', strtotime('+30 days'))
            );

            if (!$acquired) {
                $this->logOrderNote($order, sprintf(
                    'Licenzo skipped duplicate order item processing (order_id=%d, item_id=%d).',
                    $orderId,
                    (int) $item->get_id()
                ));
                continue;
            }

            try {
                if ($paymentConfirmed) {
                    $renewableSub = $this->findRenewableSubscription($customer, $product, $variation);

                    if ($renewableSub) {
                        $this->processRenewal($order, $item, $customer, $product, $variation, $renewableSub);
                    } else {
                        $this->processNewPurchase($order, $item, $customer, $product, $variation);
                    }
                } else {
                    $this->createPendingSubscription($order, $item, $customer, $product, $variation);
                }

                $this->idempotencyService->markProcessed(SubscriptionTransaction::PROVIDER_WOOCOMMERCE, $eventScope, $eventKey);
            } catch (\Throwable $e) {
                $this->idempotencyService->markFailed(SubscriptionTransaction::PROVIDER_WOOCOMMERCE, $eventScope, $eventKey);
                $this->logOrderNote($order, sprintf(
                    'Licenzo failed processing order item (order_id=%d, item_id=%d): %s',
                    $orderId,
                    (int) $item->get_id(),
                    $e->getMessage()
                ));
            }
        }
    }

    /**
     * Determine whether payment has been confirmed for the order.
     *
     * Online gateways (Stripe, PayPal) call payment_complete() which sets date_paid.
     * Offline gateways (COD, BACS, Check) do not — payment is confirmed when the
     * admin marks the order as completed.
     */
    private function isPaymentConfirmed($order): bool
    {
        if ($order->get_date_paid()) {
            return true;
        }

        if ($order->has_status('completed')) {
            return true;
        }

        return false;
    }

    /**
     * Sync linked Licenzo subscriptions when Woo order status changes.
     */
    public function syncOrderStatus(int $orderId, string $fromStatus, string $toStatus): void
    {
        if (!function_exists('wc_get_order') || $orderId <= 0) {
            return;
        }

        $order = wc_get_order($orderId);
        if (!$order) {
            return;
        }

        $subscriptionIds = $this->extractLinkedSubscriptionIdsFromOrder($order);
        if (empty($subscriptionIds)) {
            return;
        }

        $mappedStatus = $this->mapWooOrderStatusToSubscriptionStatus($toStatus);
        if ($mappedStatus === null) {
            return;
        }

        foreach ($subscriptionIds as $subscriptionId) {
            $eventScope = 'woo_order_status_sync';
            $eventKey = sprintf('%d:%d:%s:%s', $orderId, $subscriptionId, $fromStatus, $toStatus);

            $reserved = $this->idempotencyService->acquire(
                SubscriptionTransaction::PROVIDER_WOOCOMMERCE,
                $eventScope,
                $eventKey,
                $subscriptionId,
                null,
                hash('sha256', $orderId . ':' . $fromStatus . ':' . $toStatus . ':' . $subscriptionId),
                gmdate('Y-m-d H:i:s', strtotime('+30 days'))
            );

            if (!$reserved) {
                continue;
            }

            try {
                $subscription = Subscription::find($subscriptionId);
                if (!$subscription) {
                    $this->idempotencyService->markFailed(SubscriptionTransaction::PROVIDER_WOOCOMMERCE, $eventScope, $eventKey);
                    continue;
                }

                $currentStatus = (string) $subscription->status;
                if ($currentStatus !== $mappedStatus) {
                    $changes = ['from' => $currentStatus, 'to' => $mappedStatus];

                    DB::transaction(function () use ($subscription, $mappedStatus, $changes, $orderId, $fromStatus, $toStatus) {
                        $subscription->status = $mappedStatus;
                        if ($mappedStatus === Subscription::STATUS_CANCELED) {
                            $subscription->canceled_at = gmdate('Y-m-d H:i:s');
                        }
                        if ($mappedStatus === Subscription::STATUS_ACTIVE && in_array($subscription->getOriginal('status'), [
                            Subscription::STATUS_CANCELED,
                            Subscription::STATUS_PAUSED,
                            Subscription::STATUS_PENDING,
                        ], true)) {
                            $subscription->restored_at = gmdate('Y-m-d H:i:s');
                        }
                        $subscription->save();

                        $this->syncLicensesForSubscriptionStatus($subscription, $mappedStatus);
                        $this->syncWordPressUserRoleForSubscription($subscription, $mappedStatus);

                        SubscriptionEvent::log($subscription, 'status_changed', null, [
                            'source' => 'woocommerce',
                            'changes' => $changes,
                            'data' => [
                                'order_id' => $orderId,
                                'from_status' => $fromStatus,
                                'to_status' => $toStatus,
                            ],
                        ]);
                    });
                }

                $this->idempotencyService->markProcessed(SubscriptionTransaction::PROVIDER_WOOCOMMERCE, $eventScope, $eventKey);
            } catch (\Throwable $e) {
                $this->idempotencyService->markFailed(SubscriptionTransaction::PROVIDER_WOOCOMMERCE, $eventScope, $eventKey);
                $this->logOrderNote($order, sprintf(
                    'Licenzo failed syncing order status to subscription (order_id=%d, subscription_id=%d): %s',
                    $orderId,
                    $subscriptionId,
                    $e->getMessage()
                ));
            }
        }
    }

    private function findRenewableSubscription(Customer $customer, Product $product, $variation): ?Subscription
    {
        return Subscription::where('customer_id', $customer->id)
            ->where('status', Subscription::STATUS_ACTIVE)
            ->whereHas('subscriptionProducts', function ($subscriptionProductQuery) use ($product, $variation) {
                $subscriptionProductQuery
                    ->where('product_id', $product->id)
                    ->where('variation_id', $variation->id);
            })
            ->first();
    }

    private function findLinkedSubscriptionForOrderItem($order, $item): ?Subscription
    {
        $candidateIds = [];

        if (method_exists($item, 'get_meta')) {
            $itemLinkedId = (int) $item->get_meta('_licenzo_subscription_id', true);
            if ($itemLinkedId > 0) {
                $candidateIds[] = $itemLinkedId;
            }
        }

        if ($order && method_exists($order, 'get_meta')) {
            $orderLinkedId = (int) $order->get_meta('_licenzo_subscription_id', true);
            if ($orderLinkedId > 0) {
                $candidateIds[] = $orderLinkedId;
            }
        }

        foreach (array_values(array_unique(array_filter(array_map('intval', $candidateIds)))) as $subscriptionId) {
            $subscription = Subscription::find($subscriptionId);
            if ($subscription) {
                return $subscription;
            }
        }

        return null;
    }

    /**
     * Resolve the customer for a Woo order. Lookup priority:
     *   1. WordPress user_id (prevents duplicates when billing email changes)
     *   2. Billing email (covers guest orders and first-time purchases)
     *   3. Create new record if neither matches
     *
     * Contact info is always refreshed from the order so changes
     * made in WooCommerce propagate automatically.
     *
     * @param \WC_Order $order
     * @return Customer|null
     */
    private function findOrCreateCustomer($order): ?Customer
    {
        $customerEmail = (string) $order->get_billing_email();
        if ($customerEmail === '') {
            return null;
        }

        $wpUserId = (int) $order->get_user_id();
        if (!$wpUserId && function_exists('get_user_by')) {
            $matchedByEmail = get_user_by('email', $customerEmail);
            if ($matchedByEmail && isset($matchedByEmail->ID)) {
                $wpUserId = (int) $matchedByEmail->ID;
            }
        }

        $customer = null;
        $createdCustomer = false;

        if ($wpUserId) {
            $customer = Customer::where('user_id', $wpUserId)->first();
        }

        if (!$customer) {
            $customer = Customer::where('email', $customerEmail)->first();
        }

        if ($customer) {
            $customer->email = $customerEmail;
            $customer->first_name = $order->get_billing_first_name();
            $customer->last_name = $order->get_billing_last_name();
            $customer->ip = $order->get_customer_ip_address();
        } else {
            $customer = Customer::create([
                'provider'   => Product::PROVIDER_WOOCOMMERCE,
                'provider_id'=> ($order->get_customer_id() > 0 ? (string) $order->get_customer_id() : null),
                'email'      => $customerEmail,
                'first_name' => $order->get_billing_first_name(),
                'last_name'  => $order->get_billing_last_name(),
                'ip'         => $order->get_customer_ip_address(),
            ]);
            $createdCustomer = true;
        }

        if ($wpUserId && (int) ($customer->user_id ?? 0) !== $wpUserId) {
            $customer->user_id = $wpUserId;
        }

        $wooCustomerId = (int) $order->get_customer_id();
        if ($wooCustomerId > 0) {
            $customer->provider = Product::PROVIDER_WOOCOMMERCE;
            $customer->provider_id = (string) $wooCustomerId;
        }

        if ($customer->isDirty()) {
            $customer->save();
        }

        if ($createdCustomer) {
            Action::do('notification_customer_welcome', [
                'source' => 'woo_order_customer_create',
                'customer' => (array) $customer->toArray(),
                'meta' => [
                    'order_id' => (int) $order->get_id(),
                ],
            ]);
        }

        return $customer;
    }

    /**
     * Resolve a Licenzo product by Woo product/variation identifiers.
     *
     * @param int $productId
     * @param int $variationId
     * @return Product|null
     */
    private function resolveProduct(int $productId, int $variationId): ?Product
    {
        return Product::where(function ($query) use ($productId, $variationId) {
            $query->where('provider_id', (string) $productId);
            if ($variationId) {
                $query->orWhereHas('variations', function ($variationQuery) use ($variationId) {
                    $variationQuery->where('provider_id', (string) $variationId);
                });
            }
        })->with('variations')->first();
    }

    /**
     * Process renewal flow for an existing active subscription.
     *
     * @param int $orderId
     * @param mixed $item
     * @param Customer $customer
     * @param Product $product
     * @param mixed $variation
     * @param Subscription $existingSubscription
     * @return void
     */
    private function processRenewal(
        $order,
        $item,
        Customer $customer,
        Product $product,
        $variation,
        Subscription $existingSubscription
    ): void {
        DB::transaction(function () use ($order, $item, $customer, $product, $variation, $existingSubscription) {
            $orderId = (int) $order->get_id();
            $pricing = $this->resolveWooOrderItemPricing($order, $item, $variation);
            $unitPrice = $pricing['unit_price'];
            $lineTotal = $pricing['line_total'];
            $nextBillingDate = $this->getNextBillingDate($variation->billing_cycle);

            $this->createOrGetOrderTransaction([
                'uuid'               => wp_generate_uuid4(),
                'provider'           => SubscriptionTransaction::PROVIDER_WOOCOMMERCE,
                'provider_id'        => $orderId,
                'provider_charge_id' => 'woo_order_' . $orderId . '_' . $item->get_id(),
                'order_id'           => $orderId,
                'order_type'         => SubscriptionTransaction::PROVIDER_WOOCOMMERCE,
                'subscription_id'    => $existingSubscription->id,
                'customer_id'        => $existingSubscription->customer_id,
                'total'              => $lineTotal,
                'status'             => SubscriptionTransaction::STATUS_SUCCESS,
                'transaction_type'   => SubscriptionTransaction::TYPE_RENEWAL,
                'payment_method'     => (string) $order->get_payment_method(),
                'payment_mode'       => $this->resolveWooPaymentMode(),
                'currency'           => (string) $order->get_currency(),
                'meta'               => [
                    'order_id' => $orderId,
                    'item_id'  => (int) $item->get_id(),
                    'is_renewal_order' => (bool) $order->get_meta('_licenzo_renewal_order', true),
                ],
            ]);

            $existingSubscription->bill_count += 1;
            $existingSubscription->total_paid += $lineTotal;
            $existingSubscription->next_billing_date = $nextBillingDate;
            $existingSubscription->expire_at = $nextBillingDate;
            $existingSubscription->recurring_amount = in_array($variation->billing_cycle, ['onetime', 'lifetime'], true) ? 0 : $lineTotal;
            $existingSubscription->billing_cycle = $variation->billing_cycle;
            $existingSubscription->currency = (string) $order->get_currency();
            $existingSubscription->amount = $lineTotal;
            $existingSubscription->current_payment_method = (string) $order->get_payment_method();
            $existingSubscription->parent_order_id = $orderId;
            $existingSubscription->provider = SubscriptionTransaction::PROVIDER_WOOCOMMERCE;
            $existingSubscription->provider_id = (string) $orderId;
            $existingSubscription->save();

            $this->persistProviderMetadataForSubscription($existingSubscription, $order, $item, true);
            $this->markWooOrderLinks($order, $item, (int) $existingSubscription->id);

            SubscriptionProduct::where('subscription_id', $existingSubscription->id)
                ->where('product_id', $product->id)
                ->where('variation_id', $variation->id)
                ->update([
                    'provider' => $product->provider,
                    'provider_id' => $variation->provider_id ?: $product->provider_id,
                    'product_name' => $product->name,
                    'variation_name' => $variation->name,
                    'currency' => (string) $order->get_currency(),
                    'price' => $unitPrice,
                    'quantity' => $item->get_quantity(),
                ]);

            $license = License::where('subscription_id', $existingSubscription->id)->first();
            if ($license) {
                $license->expire_date = $nextBillingDate;
                $license->support_till = gmdate('Y-m-d', strtotime("+{$variation->support_for} {$variation->support_for_unit}"));
                $license->save();

                $licenseProducts = LicenseProduct::where('license_id', $license->id)->get();
                foreach ($licenseProducts as $licenseProduct) {
                    $licenseProduct->expire_date = $nextBillingDate;
                    $licenseProduct->save();
                }
            } else {
                $license = License::create([
                    'uuid'             => wp_generate_uuid4(),
                    'license_key'      => LicenzoKey::generate(),
                    'customer_id'      => $customer->id,
                    'subscription_id'  => $existingSubscription->id,
                    'status'           => 'active',
                    'activation_count' => 0,
                    'activation_limit' => $variation->max_activations,
                    'expire_date'      => $nextBillingDate,
                    'support_till'     => gmdate('Y-m-d', strtotime("+{$variation->support_for} {$variation->support_for_unit}")),
                    'activated_at'     => gmdate('Y-m-d H:i:s'),
                    'config'           => null,
                ]);

                LicenseProduct::create([
                    'license_id'       => $license->id,
                    'product_id'       => $product->id,
                    'variation_id'     => $variation->id,
                    'product_name'     => $product->name,
                    'variation_name'   => $variation->name,
                    'started_at'       => gmdate('Y-m-d'),
                    'expire_date'      => $nextBillingDate,
                    'activation_count' => 0,
                ]);
            }

            $this->syncWordPressUserRoleForCustomer($customer, Subscription::STATUS_ACTIVE);

            SubscriptionEvent::log($existingSubscription, 'subscription_renewed', $customer, [
                'source' => 'WooCommerce Order ID: ' . $orderId,
                'item_id' => $item->get_id(),
            ]);

            Action::do('notification_subscription_renewed', [
                'subscription_id' => (int) $existingSubscription->id,
                'customer_id'     => (int) $customer->id,
                'customer'        => $customer->toArray(),
                'amount'          => (string) ($existingSubscription->recurring_amount ?? ''),
                'currency'        => (string) ($existingSubscription->currency ?? ''),
            ]);
        });
    }

    /**
     * Process first-time purchase flow for a Woo order item.
     *
     * @param int $orderId
     * @param mixed $item
     * @param Customer $customer
     * @param Product $product
     * @param mixed $variation
     * @return void
     */
    private function processNewPurchase($order, $item, Customer $customer, Product $product, $variation): void
    {
        DB::transaction(function () use ($order, $item, $customer, $product, $variation) {
            $orderId = (int) $order->get_id();
            $pricing = $this->resolveWooOrderItemPricing($order, $item, $variation);
            $unitPrice = $pricing['unit_price'];
            $lineTotal = $pricing['line_total'];
            $nextBillingDate = $this->getNextBillingDate($variation->billing_cycle);

            $subscription = Subscription::create([
                'uuid'                     => wp_generate_uuid4(),
                'customer_id'              => $customer->id,
                'parent_order_id'          => $orderId,
                'provider'                 => SubscriptionTransaction::PROVIDER_WOOCOMMERCE,
                'provider_id'              => (string) $orderId,
                'subscription_name'        => "Subscription for {$variation->name}",
                'currency'                 => (string) $order->get_currency(),
                'billing_cycle'            => $variation->billing_cycle,
                'recurring_amount'         => in_array($variation->billing_cycle, ['onetime', 'lifetime'], true) ? 0 : $lineTotal,
                'billing_total_cycles'     => 0,
                'bill_count'               => 1,
                'trial_days'               => $variation->trial_days,
                'trial_ends_at'            => $variation->trial_days > 0 ? gmdate('Y-m-d', strtotime("+{$variation->trial_days} days")) : null,
                'status'                   => 'active',
                'next_billing_date'        => $nextBillingDate,
                'expire_at'                => $nextBillingDate,
                'total_paid'               => $lineTotal,
                'amount'                   => $lineTotal,
                'collection_method'        => 'charge_automatically',
                'current_payment_method'   => (string) $order->get_payment_method(),
                'provider_plan_id'         => $variation->id,
                'provider_subscription_id' => 'woo_order_' . $orderId . '_' . $item->get_id(),
                'provider_response'        => [
                    'order_id' => $orderId,
                    'item_id'  => (int) $item->get_id(),
                ],
                'config'                   => null,
                'last_payment_attempt'     => gmdate('Y-m-d H:i:s'),
                'next_payment_attempt'     => gmdate('Y-m-d H:i:s', strtotime('+1 day')),
                'dunning_status'           => 'active',
            ]);

            $this->persistProviderMetadataForSubscription($subscription, $order, $item, false);
            $this->markWooOrderLinks($order, $item, (int) $subscription->id);

            SubscriptionProduct::create([
                'subscription_id' => $subscription->id,
                'product_id'      => $product->id,
                'variation_id'    => $variation->id,
                'provider'        => $product->provider,
                'provider_id'     => $variation->provider_id ?: $product->provider_id,
                'product_name'    => $product->name,
                'variation_name'  => $variation->name,
                'currency'        => (string) $order->get_currency(),
                'price'           => $unitPrice,
                'quantity'        => $item->get_quantity(),
                'product_data'    => $product->toArray(),
                'variation_data'  => $variation->toArray(),
            ]);

            SubscriptionEvent::log($subscription, 'subscription_created', $customer, [
                'source' => 'WooCommerce Order ID: ' . $orderId,
            ]);

            $this->createOrGetOrderTransaction([
                'uuid'                => wp_generate_uuid4(),
                'provider'            => SubscriptionTransaction::PROVIDER_WOOCOMMERCE,
                'provider_id'         => $orderId,
                'provider_charge_id'  => 'woo_order_' . $orderId . '_' . $item->get_id(),
                'order_id'            => $orderId,
                'order_type'          => SubscriptionTransaction::PROVIDER_WOOCOMMERCE,
                'subscription_id'     => $subscription->id,
                'customer_id'         => $subscription->customer_id,
                'total'               => $lineTotal,
                'status'              => SubscriptionTransaction::STATUS_SUCCESS,
                'transaction_type'    => SubscriptionTransaction::TYPE_PURCHASE,
                'card_last_4'         => null,
                'card_brand'          => null,
                'payment_method'      => (string) $order->get_payment_method(),
                'payment_mode'        => $this->resolveWooPaymentMode(),
                'payment_method_type' => null,
                'currency'            => (string) $order->get_currency(),
                'meta'                => [
                    'order_id' => $orderId,
                    'item_id'  => (int) $item->get_id(),
                ],
            ]);

            $license = License::create([
                'uuid'             => wp_generate_uuid4(),
                'license_key'      => LicenzoKey::generate(),
                'customer_id'      => $customer->id,
                'subscription_id'  => $subscription->id,
                'status'           => 'active',
                'activation_count' => 0,
                'activation_limit' => $variation->max_activations,
                'expire_date'      => $nextBillingDate,
                'support_till'     => gmdate('Y-m-d', strtotime("+{$variation->support_for} {$variation->support_for_unit}")),
                'activated_at'     => gmdate('Y-m-d H:i:s'),
                'config'           => null,
            ]);

            LicenseProduct::create([
                'license_id'       => $license->id,
                'product_id'       => $product->id,
                'variation_id'     => $variation->id,
                'product_name'     => $product->name,
                'variation_name'   => $variation->name,
                'started_at'       => gmdate('Y-m-d'),
                'expire_date'      => $nextBillingDate,
                'activation_count' => 0,
            ]);

            $this->syncWordPressUserRoleForCustomer($customer, Subscription::STATUS_ACTIVE);
        });
    }

    /**
     * Create a pending subscription when payment has not yet been confirmed.
     *
     * Creates Subscription (pending), SubscriptionProduct, and Transaction (pending).
     * Does NOT create License or LicenseProduct — those are deferred until payment
     * is confirmed via activatePendingSubscription().
     */
    private function createPendingSubscription($order, $item, Customer $customer, Product $product, $variation): void
    {
        DB::transaction(function () use ($order, $item, $customer, $product, $variation) {
            $orderId = (int) $order->get_id();
            $pricing = $this->resolveWooOrderItemPricing($order, $item, $variation);
            $unitPrice = $pricing['unit_price'];
            $lineTotal = $pricing['line_total'];
            $nextBillingDate = $this->getNextBillingDate($variation->billing_cycle);

            $subscription = Subscription::create([
                'uuid'                     => wp_generate_uuid4(),
                'customer_id'              => $customer->id,
                'parent_order_id'          => $orderId,
                'provider'                 => SubscriptionTransaction::PROVIDER_WOOCOMMERCE,
                'provider_id'              => (string) $orderId,
                'subscription_name'        => "Subscription for {$variation->name}",
                'currency'                 => (string) $order->get_currency(),
                'billing_cycle'            => $variation->billing_cycle,
                'recurring_amount'         => in_array($variation->billing_cycle, ['onetime', 'lifetime'], true) ? 0 : $lineTotal,
                'billing_total_cycles'     => 0,
                'bill_count'               => 0,
                'trial_days'               => $variation->trial_days,
                'trial_ends_at'            => $variation->trial_days > 0 ? gmdate('Y-m-d', strtotime("+{$variation->trial_days} days")) : null,
                'status'                   => Subscription::STATUS_PENDING,
                'next_billing_date'        => $nextBillingDate,
                'expire_at'                => $nextBillingDate,
                'total_paid'               => 0,
                'amount'                   => $lineTotal,
                'collection_method'        => 'charge_automatically',
                'current_payment_method'   => (string) $order->get_payment_method(),
                'provider_plan_id'         => $variation->id,
                'provider_subscription_id' => 'woo_order_' . $orderId . '_' . $item->get_id(),
                'provider_response'        => [
                    'order_id' => $orderId,
                    'item_id'  => (int) $item->get_id(),
                ],
                'config'                   => null,
                'dunning_status'           => 'pending',
            ]);

            $this->markWooOrderLinks($order, $item, (int) $subscription->id);

            SubscriptionProduct::create([
                'subscription_id' => $subscription->id,
                'product_id'      => $product->id,
                'variation_id'    => $variation->id,
                'provider'        => $product->provider,
                'provider_id'     => $variation->provider_id ?: $product->provider_id,
                'product_name'    => $product->name,
                'variation_name'  => $variation->name,
                'currency'        => (string) $order->get_currency(),
                'price'           => $unitPrice,
                'quantity'        => $item->get_quantity(),
                'product_data'    => $product->toArray(),
                'variation_data'  => $variation->toArray(),
            ]);

            SubscriptionEvent::log($subscription, 'subscription_created', $customer, [
                'source' => 'WooCommerce Order ID: ' . $orderId,
                'note'   => 'Awaiting payment confirmation',
            ]);

            $this->createOrGetOrderTransaction([
                'uuid'                => wp_generate_uuid4(),
                'provider'            => SubscriptionTransaction::PROVIDER_WOOCOMMERCE,
                'provider_id'         => $orderId,
                'provider_charge_id'  => 'woo_order_' . $orderId . '_' . $item->get_id(),
                'order_id'            => $orderId,
                'order_type'          => SubscriptionTransaction::PROVIDER_WOOCOMMERCE,
                'subscription_id'     => $subscription->id,
                'customer_id'         => $customer->id,
                'total'               => $lineTotal,
                'status'              => SubscriptionTransaction::STATUS_PENDING,
                'transaction_type'    => SubscriptionTransaction::TYPE_PURCHASE,
                'card_last_4'         => null,
                'card_brand'          => null,
                'payment_method'      => (string) $order->get_payment_method(),
                'payment_mode'        => $this->resolveWooPaymentMode(),
                'payment_method_type' => null,
                'currency'            => (string) $order->get_currency(),
                'meta'                => [
                    'order_id' => $orderId,
                    'item_id'  => (int) $item->get_id(),
                    'pending'  => true,
                ],
            ]);

            $this->logOrderNote($order, sprintf(
                'Licenzo created pending subscription (ID: %d) — awaiting payment confirmation.',
                $subscription->id
            ));
        });
    }

    /**
     * Activate a pending subscription after payment has been confirmed.
     *
     * Transitions Subscription to active, marks Transaction as success,
     * creates License + LicenseProduct, and syncs WordPress user role.
     */
    private function activatePendingSubscription(
        Subscription $subscription,
        $order,
        $item,
        Customer $customer,
        Product $product,
        $variation
    ): void {
        DB::transaction(function () use ($subscription, $order, $item, $customer, $product, $variation) {
            $orderId = (int) $order->get_id();
            $pricing = $this->resolveWooOrderItemPricing($order, $item, $variation);
            $lineTotal = $pricing['line_total'];
            $nextBillingDate = $this->getNextBillingDate($variation->billing_cycle);

            $subscription->status = Subscription::STATUS_ACTIVE;
            $subscription->total_paid = $lineTotal;
            $subscription->bill_count = 1;
            $subscription->last_payment_attempt = gmdate('Y-m-d H:i:s');
            $subscription->next_payment_attempt = gmdate('Y-m-d H:i:s', strtotime('+1 day'));
            $subscription->dunning_status = 'active';
            $subscription->save();

            $this->persistProviderMetadataForSubscription($subscription, $order, $item, false);

            $chargeId = 'woo_order_' . $orderId . '_' . $item->get_id();
            SubscriptionTransaction::where('subscription_id', $subscription->id)
                ->where('provider', SubscriptionTransaction::PROVIDER_WOOCOMMERCE)
                ->where('provider_charge_id', $chargeId)
                ->where('status', SubscriptionTransaction::STATUS_PENDING)
                ->update([
                    'status' => SubscriptionTransaction::STATUS_SUCCESS,
                ]);

            $license = License::create([
                'uuid'             => wp_generate_uuid4(),
                'license_key'      => LicenzoKey::generate(),
                'customer_id'      => $customer->id,
                'subscription_id'  => $subscription->id,
                'status'           => 'active',
                'activation_count' => 0,
                'activation_limit' => $variation->max_activations,
                'expire_date'      => $nextBillingDate,
                'support_till'     => gmdate('Y-m-d', strtotime("+{$variation->support_for} {$variation->support_for_unit}")),
                'activated_at'     => gmdate('Y-m-d H:i:s'),
                'config'           => null,
            ]);

            LicenseProduct::create([
                'license_id'       => $license->id,
                'product_id'       => $product->id,
                'variation_id'     => $variation->id,
                'product_name'     => $product->name,
                'variation_name'   => $variation->name,
                'started_at'       => gmdate('Y-m-d'),
                'expire_date'      => $nextBillingDate,
                'activation_count' => 0,
            ]);

            $this->syncWordPressUserRoleForCustomer($customer, Subscription::STATUS_ACTIVE);

            SubscriptionEvent::log($subscription, 'subscription_activated', $customer, [
                'source' => 'Payment confirmed — WooCommerce Order ID: ' . $orderId,
            ]);

            $this->logOrderNote($order, sprintf(
                'Licenzo activated subscription (ID: %d) and generated license key.',
                $subscription->id
            ));
        });
    }

    /**
     * Resolve next billing date based on price cycle.
     *
     * @param string $cycle
     * @return string|null
     */
    private function getNextBillingDate(string $cycle): ?string
    {
        switch ($cycle) {
            case 'weekly':
                return gmdate('Y-m-d', strtotime('+1 week'));
            case 'monthly':
                return gmdate('Y-m-d', strtotime('+1 month'));
            case 'yearly':
                return gmdate('Y-m-d', strtotime('+1 year'));
            case 'onetime':
            case 'lifetime':
            default:
                return null;
        }
    }

    /**
     * Resolve actual paid amounts from Woo order item (fallback to mapped variation price).
     *
     * @return array{unit_price:float,line_total:float}
     */
    private function resolveWooOrderItemPricing($order, $item, $variation): array
    {
        $quantity = max(1, (int) $item->get_quantity());
        $lineTotal = (float) $item->get_total();

        if ($lineTotal <= 0) {
            $fallbackUnit = (float) ($variation->sale_price ?: $variation->regular_price ?: 0);
            $lineTotal = $fallbackUnit * $quantity;
        }

        $unitPrice = $lineTotal / $quantity;

        return [
            'unit_price' => (float) $unitPrice,
            'line_total' => (float) $lineTotal,
        ];
    }

    private function resolveWooPaymentMode(): string
    {
        $settings = get_option('woocommerce_stripe_settings', []);
        if (is_array($settings) && isset($settings['testmode']) && in_array($settings['testmode'], ['yes', '1', 1, true], true)) {
            return SubscriptionTransaction::PAYMENT_MODE_TEST;
        }

        return SubscriptionTransaction::PAYMENT_MODE_LIVE;
    }

    private function persistProviderMetadataForSubscription(Subscription $subscription, $order, $item, bool $isRenewal): void
    {
        $stripeMeta = $this->extractWooStripeMeta($order);
        $paymentMethod = (string) $order->get_payment_method();
        $stripeCustomerId = (string) ($stripeMeta['_stripe_customer_id'] ?? '');

        if ($stripeCustomerId !== '') {
            $subscription->provider_customer_id = $stripeCustomerId;
        }

        if ($paymentMethod !== '') {
            $subscription->current_payment_method = $paymentMethod;
        }

        $hasStripeAutoRenewMaterial = $stripeCustomerId !== ''
            || (string) ($stripeMeta['_stripe_source_id'] ?? '') !== ''
            || (string) ($stripeMeta['_stripe_card_id'] ?? '') !== ''
            || (string) ($stripeMeta['_stripe_mandate_id'] ?? '') !== '';

        if ($hasStripeAutoRenewMaterial) {
            $subscription->collection_method = Subscription::COLLECTION_CHARGE_AUTOMATICALLY;
        }

        $providerResponse = is_array($subscription->provider_response) ? $subscription->provider_response : [];
        $providerResponse['woo'] = [
            'order_id' => (int) $order->get_id(),
            'item_id' => (int) $item->get_id(),
            'payment_method' => $paymentMethod,
            'payment_method_title' => (string) $order->get_payment_method_title(),
            'currency' => (string) $order->get_currency(),
            'is_renewal' => $isRenewal,
            'renewal_order' => (bool) $order->get_meta('_licenzo_renewal_order', true),
            'stripe_meta' => $stripeMeta,
        ];

        if (!empty($stripeMeta['_stripe_subscription_id'])) {
            $providerResponse['woo']['stripe_subscription_id'] = (string) $stripeMeta['_stripe_subscription_id'];
        }

        $subscription->provider_response = $providerResponse;
        $subscription->save();

        SubscriptionMeta::setMeta($subscription->id, 'woo_source_order_id', (int) $order->get_id());
        SubscriptionMeta::setMeta($subscription->id, 'woo_last_order_item_id', (int) $item->get_id());
        SubscriptionMeta::setMeta($subscription->id, 'woo_payment_method', $paymentMethod);
        if (!empty($stripeMeta)) {
            SubscriptionMeta::setMeta($subscription->id, 'woo_stripe_meta', $stripeMeta);
        }
    }

    private function extractWooStripeMeta($order): array
    {
        $keys = [
            '_stripe_customer_id',
            '_stripe_source_id',
            '_stripe_card_id',
            '_stripe_mandate_id',
            '_stripe_setup_intent',
            '_stripe_upe_payment_type',
            '_stripe_subscription_id',
        ];

        $result = [];
        foreach ($keys as $key) {
            $value = (string) $order->get_meta($key, true);
            if ($value !== '') {
                $result[$key] = $value;
            }
        }

        return $result;
    }

    private function markWooOrderLinks($order, $item, int $subscriptionId): void
    {
        if ($subscriptionId <= 0) {
            return;
        }

        if (method_exists($item, 'update_meta_data')) {
            $item->update_meta_data('_licenzo_subscription_id', $subscriptionId);
            $item->save();
        }

        $orderIds = $order->get_meta('_licenzo_subscription_ids', true);
        if (!is_array($orderIds)) {
            $orderIds = [];
        }

        $orderIds[] = $subscriptionId;
        $orderIds = array_values(array_unique(array_map('intval', $orderIds)));

        $order->update_meta_data('_licenzo_subscription_ids', $orderIds);
        if (count($orderIds) === 1) {
            $order->update_meta_data('_licenzo_subscription_id', $orderIds[0]);
        }
        $order->save();
    }

    private function extractLinkedSubscriptionIdsFromOrder($order): array
    {
        $ids = [];

        $single = (int) $order->get_meta('_licenzo_subscription_id', true);
        if ($single > 0) {
            $ids[] = $single;
        }

        $multi = $order->get_meta('_licenzo_subscription_ids', true);
        if (is_array($multi)) {
            foreach ($multi as $id) {
                $id = (int) $id;
                if ($id > 0) {
                    $ids[] = $id;
                }
            }
        }

        foreach ($order->get_items() as $item) {
            if (!method_exists($item, 'get_meta')) {
                continue;
            }

            $id = (int) $item->get_meta('_licenzo_subscription_id', true);
            if ($id > 0) {
                $ids[] = $id;
            }
        }

        $ids = array_values(array_unique(array_filter(array_map('intval', $ids))));
        sort($ids);

        return $ids;
    }

    private function mapWooOrderStatusToSubscriptionStatus(string $wooStatus): ?string
    {
        switch ($wooStatus) {
            case 'processing':
            case 'completed':
                return Subscription::STATUS_ACTIVE;

            case 'pending':
            case 'on-hold':
                return Subscription::STATUS_PENDING;

            case 'failed':
            case 'cancelled':
            case 'refunded':
                return Subscription::STATUS_CANCELED;

            default:
                return null;
        }
    }

    private function syncLicensesForSubscriptionStatus(Subscription $subscription, string $status): void
    {
        $licenseStatus = $status === Subscription::STATUS_ACTIVE ? 'active' : 'inactive';

        $licenses = License::where('subscription_id', $subscription->id)->get();
        foreach ($licenses as $license) {
            $license->status = $licenseStatus;
            $license->save();
        }
    }

    private function createOrGetOrderTransaction(array $attributes): SubscriptionTransaction
    {
        $query = SubscriptionTransaction::where('provider', $attributes['provider'])
            ->where('provider_charge_id', $attributes['provider_charge_id']);

        $existing = $query->first();
        if ($existing) {
            return $existing;
        }

        try {
            SubscriptionTransaction::create($attributes);
        } catch (\Throwable $e) {
            // Ignore and fall through to lookup by unique key.
        }

        $persisted = SubscriptionTransaction::where('provider', $attributes['provider'])
            ->where('provider_charge_id', $attributes['provider_charge_id'])
            ->first();

        if ($persisted) {
            return $persisted;
        }

        throw new \RuntimeException('Failed to create or resolve subscription transaction by unique provider charge key.');
    }

    private function buildOrderItemEventKey(int $orderId, $item): string
    {
        return sprintf('%d:%d', $orderId, (int) $item->get_id());
    }

    private function buildOrderItemChecksum(int $orderId, $item): string
    {
        return hash('sha256', wp_json_encode([
            'order_id' => $orderId,
            'item_id' => (int) $item->get_id(),
            'product_id' => (int) $item->get_product_id(),
            'variation_id' => (int) $item->get_variation_id(),
            'quantity' => (float) $item->get_quantity(),
            'total' => (string) $item->get_total(),
        ]));
    }

    private function logOrderNote($order, string $message): void
    {
        if (!method_exists($order, 'add_order_note')) {
            return;
        }

        $order->add_order_note($message);
    }

    private function syncWordPressUserRoleForSubscription(Subscription $subscription, string $status): void
    {
        $customerId = (int) ($subscription->customer_id ?? 0);
        if ($customerId <= 0) {
            return;
        }

        $customer = Customer::find($customerId);
        if (!$customer) {
            return;
        }

        $this->syncWordPressUserRoleForCustomer($customer, $status);
    }

    private function syncWordPressUserRoleForCustomer(Customer $customer, string $status): void
    {
        $wpUserId = (int) ($customer->user_id ?? 0);
        if ($wpUserId <= 0 || !function_exists('get_userdata')) {
            return;
        }

        $wpUser = get_userdata($wpUserId);
        if (!$wpUser || !($wpUser instanceof \WP_User)) {
            return;
        }

        // Avoid clobbering privileged/admin users during subscription status sync.
        if (user_can($wpUser, 'manage_options') || user_can($wpUser, 'manage_woocommerce')) {
            return;
        }

        $settings = Settings::getSubscriptionSettings();
        $roleKey = $status === Subscription::STATUS_ACTIVE ? 'active_role' : 'inactive_role';
        $targetRole = sanitize_key((string) ($settings[$roleKey] ?? ''));

        if ($targetRole === '') {
            return;
        }

        $roles = function_exists('wp_roles') ? wp_roles() : null;
        if (!$roles || !isset($roles->roles) || !is_array($roles->roles) || !isset($roles->roles[$targetRole])) {
            return;
        }

        $currentRoles = is_array($wpUser->roles ?? null) ? $wpUser->roles : [];
        if (count($currentRoles) === 1 && in_array($targetRole, $currentRoles, true)) {
            return;
        }

        $wpUser->set_role($targetRole);
    }
}
