<?php

namespace Licenzo\Modules\WooCommerce;

use Licenzo\Helper\Settings;

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

class WooStripeRenewalPaymentService
{
    /**
     * @var WooStripeCompatibilityService
     */
    private $compatibility;

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

    public function isAvailable(): bool
    {
        return function_exists('woocommerce_gateway_stripe')
            && class_exists('WC_Stripe')
            && class_exists('WC_Stripe_Payment_Gateway');
    }

    /**
     * Attempt an off-session renewal charge using Woo Stripe internals.
     *
     * @param \WC_Order $renewalOrder
     * @return array<string,mixed>
     */
    public function chargeRenewalOrder($renewalOrder): array
    {
        if (!$this->isStripeAutoRenewEnabled()) {
            if ($renewalOrder && method_exists($renewalOrder, 'add_order_note')) {
                $renewalOrder->add_order_note('Licenzo renewal auto-charge skipped: Stripe Auto Renewal is disabled in Subscription Settings.');
            }

            return [
                'success' => false,
                'mode' => 'manual',
                'message' => 'Stripe auto renewal is disabled in Subscription Settings.',
            ];
        }

        if (!$this->isAvailable()) {
            return [
                'success' => false,
                'mode' => 'manual',
                'message' => 'WooCommerce Stripe is unavailable.',
            ];
        }

        if (!$renewalOrder || !method_exists($renewalOrder, 'get_id')) {
            return [
                'success' => false,
                'mode' => 'manual',
                'message' => 'Invalid renewal order.',
            ];
        }

        if (!$this->compatibility->isStripeAutoRenewCapableOrder($renewalOrder)) {
            return [
                'success' => false,
                'mode' => 'manual',
                'message' => 'Renewal order is not Stripe auto-renew capable.',
            ];
        }

        $gateway = $this->getMainGateway();
        if (!$gateway) {
            return [
                'success' => false,
                'mode' => 'manual',
                'message' => 'Stripe gateway instance not available.',
            ];
        }

        try {
            $this->validateMinimumOrderAmount($gateway, $renewalOrder);

            $preparedSource = $gateway->prepare_order_source($renewalOrder);
            $customerId = is_object($preparedSource) && isset($preparedSource->customer)
                ? (string) $preparedSource->customer
                : '';

            if ($customerId === '') {
                $this->addOrderNote($renewalOrder, 'Licenzo renewal auto-charge skipped: missing Stripe customer on renewal order.');
                return [
                    'success' => false,
                    'mode' => 'manual',
                    'message' => 'Stripe customer not found on renewal order.',
                ];
            }

            $intent = $gateway->create_intent($renewalOrder, $preparedSource);

            if (empty($intent->error)) {
                $orderWasAlreadyLocked = $this->lockOrderPayment($gateway, $renewalOrder);
                if ($orderWasAlreadyLocked) {
                    $this->addOrderNote($renewalOrder, 'Licenzo renewal auto-charge skipped: Stripe order payment lock already present.');
                    return [
                        'success' => false,
                        'mode' => 'stripe',
                        'message' => 'Stripe order payment is already locked.',
                    ];
                }

                if (
                    class_exists('WC_Stripe_Intent_Status') &&
                    isset($intent->status) &&
                    \WC_Stripe_Intent_Status::REQUIRES_CONFIRMATION === $intent->status
                ) {
                    $intent = $this->confirmIntentWithMandateSupport($gateway, $intent, $renewalOrder, $preparedSource);
                }
            }

            if (!empty($intent->error)) {
                $gateway->maybe_remove_non_existent_customer($intent->error, $renewalOrder);
                $this->unlockOrderPayment($gateway, $renewalOrder);

                $message = $this->extractIntentErrorMessage($intent);
                $this->addOrderNote($renewalOrder, 'Licenzo renewal auto-charge failed: ' . $message);

                return [
                    'success' => false,
                    'mode' => 'stripe',
                    'message' => $message,
                    'intent' => $this->normalizeIntentSummary($intent),
                ];
            }

            $status = isset($intent->status) ? (string) $intent->status : '';

            if ($status === 'requires_action' || $status === 'requires_confirmation') {
                $this->unlockOrderPayment($gateway, $renewalOrder);
                $this->addOrderNote($renewalOrder, 'Licenzo renewal auto-charge requires customer action (3DS/SCA). Status: ' . $status);

                return [
                    'success' => false,
                    'mode'    => 'stripe',
                    'message' => 'PaymentIntent requires customer action (' . $status . ').',
                    'intent'  => $this->normalizeIntentSummary($intent),
                ];
            }

            if (!empty($intent)) {
                $response = $gateway->get_latest_charge_from_intent($intent);
                $gateway->process_response($response, $renewalOrder);
            }

            if ($status !== 'succeeded') {
                $this->unlockOrderPayment($gateway, $renewalOrder);
                $this->addOrderNote($renewalOrder, 'Licenzo renewal auto-charge ended with unexpected status: ' . $status);

                return [
                    'success' => false,
                    'mode'    => 'stripe',
                    'message' => 'PaymentIntent ended with status: ' . $status,
                    'intent'  => $this->normalizeIntentSummary($intent),
                ];
            }

            $this->unlockOrderPayment($gateway, $renewalOrder);

            return [
                'success' => true,
                'mode' => 'stripe',
                'message' => 'Renewal charge dispatched via Woo Stripe.',
                'order_status' => method_exists($renewalOrder, 'get_status') ? (string) $renewalOrder->get_status() : null,
                'intent' => $this->normalizeIntentSummary($intent ?? null),
            ];
        } catch (\Throwable $e) {
            if ($gateway) {
                try {
                    $this->unlockOrderPayment($gateway, $renewalOrder);
                } catch (\Throwable $unlockError) {
                    // no-op
                }
            }

            $this->addOrderNote($renewalOrder, 'Licenzo renewal auto-charge exception: ' . $e->getMessage());

            if (class_exists('WC_Stripe_Logger')) {
                \WC_Stripe_Logger::log('Licenzo renewal auto-charge exception: ' . $e->getMessage());
            }

            return [
                'success' => false,
                'mode' => 'stripe',
                'message' => $e->getMessage(),
            ];
        }
    }

    private function getMainGateway()
    {
        if (!$this->isAvailable()) {
            return null;
        }

        $stripe = \WC_Stripe::get_instance();
        if (!$stripe || !method_exists($stripe, 'get_main_stripe_gateway')) {
            return null;
        }

        $gateway = $stripe->get_main_stripe_gateway();
        if (!$gateway) {
            return null;
        }

        return $gateway;
    }

    private function getOrderHelper()
    {
        if (!class_exists('WC_Stripe_Order_Helper') || !method_exists('WC_Stripe_Order_Helper', 'get_instance')) {
            return null;
        }

        try {
            return \WC_Stripe_Order_Helper::get_instance();
        } catch (\Throwable $e) {
            return null;
        }
    }

    private function validateMinimumOrderAmount($gateway, $order): void
    {
        $orderHelper = $this->getOrderHelper();

        if ($orderHelper && method_exists($orderHelper, 'validate_minimum_order_amount')) {
            $orderHelper->validate_minimum_order_amount($order);
            return;
        }

        if (method_exists($gateway, 'validate_minimum_order_amount')) {
            $gateway->validate_minimum_order_amount($order);
        }
    }

    /**
     * Returns true when order is already locked.
     */
    private function lockOrderPayment($gateway, $order): bool
    {
        $orderHelper = $this->getOrderHelper();

        if ($orderHelper && method_exists($orderHelper, 'lock_order_payment')) {
            return (bool) $orderHelper->lock_order_payment($order);
        }

        if (method_exists($gateway, 'lock_order_payment')) {
            return (bool) $gateway->lock_order_payment($order);
        }

        return false;
    }

    private function unlockOrderPayment($gateway, $order): void
    {
        $orderHelper = $this->getOrderHelper();

        if ($orderHelper && method_exists($orderHelper, 'unlock_order_payment')) {
            $orderHelper->unlock_order_payment($order);
            return;
        }

        if (method_exists($gateway, 'unlock_order_payment')) {
            $gateway->unlock_order_payment($order);
        }
    }

    private function confirmIntentWithMandateSupport($gateway, $intent, $order, $preparedSource)
    {
        if (
            !class_exists('WC_Stripe_Intent_Status')
            || !isset($intent->status)
            || \WC_Stripe_Intent_Status::REQUIRES_CONFIRMATION !== $intent->status
        ) {
            return $intent;
        }

        if (
            !class_exists('WC_Stripe_Helper')
            || !class_exists('WC_Stripe_API')
            || !isset($preparedSource->source)
        ) {
            return $gateway->confirm_intent($intent, $order, $preparedSource);
        }

        $confirmRequest = \WC_Stripe_Helper::add_payment_method_to_request_array($preparedSource->source, []);

        $paymentMethodTypes = [];
        if (isset($intent->payment_method_types) && is_array($intent->payment_method_types)) {
            $paymentMethodTypes = $intent->payment_method_types;
        } elseif (isset($preparedSource->source_object->type)) {
            $paymentMethodTypes = [$preparedSource->source_object->type];
        }

        if (in_array('sepa_debit', $paymentMethodTypes, true)) {
            $confirmRequest['mandate_data'] = [
                'customer_acceptance' => [
                    'type' => 'offline',
                ],
            ];
        }

        $level3Data = method_exists($gateway, 'get_level3_data_from_order')
            ? $gateway->get_level3_data_from_order($order)
            : [];

        $confirmedIntent = \WC_Stripe_API::request_with_level3_data(
            $confirmRequest,
            'payment_intents/' . $intent->id . '/confirm',
            $level3Data,
            $order
        );

        return $confirmedIntent;
    }

    private function extractIntentErrorMessage($intent): string
    {
        if (!is_object($intent) || !isset($intent->error)) {
            return 'Unknown Stripe error';
        }

        if (is_object($intent->error) && isset($intent->error->message)) {
            return (string) $intent->error->message;
        }

        if (is_array($intent->error) && isset($intent->error['message'])) {
            return (string) $intent->error['message'];
        }

        return 'Unknown Stripe error';
    }

    private function normalizeIntentSummary($intent): ?array
    {
        if (!is_object($intent)) {
            return null;
        }

        return [
            'id' => isset($intent->id) ? (string) $intent->id : null,
            'status' => isset($intent->status) ? (string) $intent->status : null,
        ];
    }

    private function addOrderNote($order, string $message): void
    {
        if ($order && method_exists($order, 'add_order_note')) {
            $order->add_order_note($message);
        }
    }

    private function isStripeAutoRenewEnabled(): bool
    {
        $settings = Settings::getSubscriptionSettings();
        return (($settings['stripe_auto_renew'] ?? 'yes') === 'yes');
    }
}
