<?php

/**
 * Refunds Hooks
 *
 * Integrates with WooCommerce native refund system to handle
 * subscription-related refunds.
 *
 * @package ArraySubs\Features\Refunds\Services
 * @since 1.0.0
 */

namespace ArraySubs\Features\Refunds\Services;

defined('ABSPATH') || exit;

/**
 * Refunds hooks integration
 */
class Hooks
{
    /**
     * Enable auto-loading via AbstractLoader
     *
     * @var bool
     */
    public static $loadable = true;

    /**
     * Constructor - Register hooks
     */
    public function __construct()
    {
        // Hook into WooCommerce refund creation
        add_action('woocommerce_order_refunded', [$this, 'onOrderRefunded'], 10, 2);

        // WooCommerce determines this before the generic refunded hook runs.
        add_action('woocommerce_order_fully_refunded', [$this, 'onOrderFullyRefunded'], 10, 2);

        // Fallback for orders moved to Refunded without a normal refund object.
        add_action('woocommerce_order_status_refunded', [$this, 'onOrderStatusRefunded'], 20, 2);

        // Hook into refund creation for more details
        add_action('woocommerce_refund_created', [$this, 'onRefundCreated'], 10, 2);

        // Handle subscription cancellation after full refund
        add_action('arraysubs_subscription_fully_refunded', [$this, 'handleFullRefund'], 10, 2);

        // Add refund info to order edit screen
        add_action('woocommerce_admin_order_data_after_billing_address', [$this, 'displaySubscriptionRefundInfo']);

        // Add prorated refund button to subscription admin
        add_action('arraysubs_admin_subscription_actions', [$this, 'addProratedRefundAction'], 10, 2);
    }

    /**
     * Handle WooCommerce order refunded event
     *
     * @param int $order_id   Order ID
     * @param int $refund_id  Refund ID
     */
    public function onOrderRefunded(int $order_id, int $refund_id): void
    {
        $order = wc_get_order($order_id);
        if (!$order) {
            return;
        }

        $subscription_ids = $this->getSubscriptionIdsForOrder($order);
        if (empty($subscription_ids)) {
            return;
        }

        // Get refund details
        $refund = wc_get_order($refund_id);
        if (!$refund) {
            return;
        }

        $refund_amount = abs($refund->get_total());
        $refund_reason = $refund->get_reason();

        foreach ($subscription_ids as $subscription_id) {
            $subscription = arraysubs_get_subscription(absint($subscription_id));
            if (!$subscription instanceof \WP_Post) {
                continue;
            }

            // Log the refund
            $this->logRefund($subscription_id, $order_id, $refund_id, $refund_amount, $refund_reason);

            // Add subscription note
            if (function_exists('arraysubs_add_subscription_note')) {
                arraysubs_add_subscription_note(
                    absint($subscription_id),
                    sprintf(
                        /* translators: 1: refund amount, 2: order ID, 3: reason */
                        __('Refund processed: %1$s for order #%2$d. Reason: %3$s', 'arraysubs'),
                        wc_price($refund_amount),
                        $order_id,
                        $refund_reason ?: __('No reason provided', 'arraysubs')
                    ),
                    'system',
                    0,
                    [],
                    'subscription'
                );
            }

            $this->checkForFullRefund($subscription_id, $order_id, $order, $refund_amount);

            // Fire action for extensions
            do_action('arraysubs_subscription_order_refunded', $subscription_id, $order_id, $refund_id, $refund_amount);
        }
    }

    /**
     * Handle WooCommerce's definitive full-refund event.
     *
     * @param int $order_id  Order ID.
     * @param int $refund_id Refund ID.
     */
    public function onOrderFullyRefunded(int $order_id, int $refund_id): void
    {
        $order = wc_get_order($order_id);
        if (!$order instanceof \WC_Order) {
            return;
        }

        $this->processFullyRefundedOrder($order);
    }

    /**
     * Handle orders manually/gateway-set to Refunded status.
     *
     * @param int       $order_id Order ID.
     * @param \WC_Order $order    Order object.
     */
    public function onOrderStatusRefunded(int $order_id, $order): void
    {
        if (!$order instanceof \WC_Order) {
            $order = wc_get_order($order_id);
        }

        if (!$order instanceof \WC_Order) {
            return;
        }

        $this->processFullyRefundedOrder($order);
    }

    /**
     * Handle refund created event (more detailed)
     *
     * @param int   $refund_id Refund ID
     * @param array $args      Refund arguments
     */
    public function onRefundCreated(int $refund_id, array $args): void
    {
        $order_id = $args['order_id'] ?? 0;
        if (!$order_id) {
            return;
        }

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

        $subscription_ids = $this->getSubscriptionIdsForOrder($order);
        $subscription_id = !empty($subscription_ids) ? $subscription_ids[0] : 0;

        if (!$subscription_id) {
            return;
        }

        // Stamp the refund order with its subscription linkage so admin
        // refund views and reports can join back to the subscription.
        $refund = wc_get_order($refund_id);
        if ($refund) {
            $refund->update_meta_data('_subscription_id', absint($subscription_id));
            $refund->save();
        }
    }

    /**
     * Handle full refund of subscription
     *
     * @param int $subscription_id Subscription ID
     * @param int $order_id        Order ID
     */
    public function handleFullRefund(int $subscription_id, int $order_id): void
    {
        $subscription = arraysubs_get_subscription($subscription_id);
        if (!$subscription instanceof \WP_Post) {
            return;
        }

        if (in_array($subscription->post_status, ['arraysubs-cancelled', 'arraysubs-expired'], true)) {
            return;
        }

        // A completed full refund means the paid entitlement has been
        // reversed. Keep this independent from "refund on cancellation"
        // settings, which control the opposite direction: cancellation -> refund.
        $this->cancelSubscriptionAfterRefund($subscription_id, $order_id);

        // Fire action for extensions
        do_action('arraysubs_subscription_fully_refunded_processed', $subscription_id, $order_id, 'immediate');
    }

    /**
     * Check if order is fully refunded
     *
     * @param int       $subscription_id Subscription ID
     * @param int       $order_id        Order ID
     * @param \WC_Order $order           Order object
     * @param float     $refund_amount   Latest refund amount
     */
    private function checkForFullRefund(int $subscription_id, int $order_id, \WC_Order $order, float $refund_amount): void
    {
        $order_total = floatval($order->get_total());
        $total_refunded = floatval($order->get_total_refunded());

        // Check if fully refunded (with small tolerance for rounding)
        if ($order_total > 0 && ($order_total - $total_refunded) < 0.01) {
            $this->markOrderRefunded($order);

            // Fire full refund action
            do_action('arraysubs_subscription_fully_refunded', $subscription_id, $order_id);
        }
    }

    /**
     * Cancel all subscriptions linked to a fully refunded order.
     *
     * @param \WC_Order $order Order object.
     */
    private function processFullyRefundedOrder(\WC_Order $order): void
    {
        $subscription_ids = $this->getSubscriptionIdsForOrder($order);
        if (empty($subscription_ids)) {
            return;
        }

        foreach ($subscription_ids as $subscription_id) {
            do_action('arraysubs_subscription_fully_refunded', absint($subscription_id), $order->get_id());
        }
    }

    /**
     * Resolve subscription IDs for an order.
     *
     * @param \WC_Order $order Order object.
     * @return int[]
     */
    private function getSubscriptionIdsForOrder(\WC_Order $order): array
    {
        $linked = function_exists('arraysubs_get_subscription_ids_for_order')
            ? arraysubs_get_subscription_ids_for_order($order)
            : [];

        if (empty($linked)) {
            $fallback = $this->findSubscriptionByParentOrder($order->get_id());
            if ($fallback) {
                $linked[] = $fallback;
            }
        }

        return array_values(array_unique(array_map('absint', array_filter($linked))));
    }

    /**
     * Ensure a fully-refunded Woo order exposes the canonical Refunded status.
     *
     * @param \WC_Order $order Order object.
     */
    private function markOrderRefunded(\WC_Order $order): void
    {
        if ($order->get_status() === 'refunded') {
            return;
        }

        $order->update_status(
            'refunded',
            __('Order fully refunded; ArraySubs synchronized subscription state.', 'arraysubs'),
            true
        );
    }

    /**
     * Cancel subscription after full refund
     *
     * Delegates to the canonical helper so the lifecycle meta, scheduled-
     * cancel override detection, retry-state cleanup, and AS unscheduling
     * all happen in one place.
     *
     * @param int $subscription_id Subscription ID
     * @param int $order_id        Order ID
     */
    private function cancelSubscriptionAfterRefund(int $subscription_id, int $order_id): void
    {
        // Refund-specific context (set BEFORE the helper so AutoNotes can
        // observe it during the synchronous status transition).
        update_post_meta($subscription_id, '_refund_cancellation_order_id', $order_id);
        update_post_meta($subscription_id, '_end_date', current_time('mysql', true));

        $cancelled = arraysubs_cancel_subscription_immediately(
            $subscription_id,
            __('Full refund processed', 'arraysubs'),
            'system'
        );

        if (!$cancelled) {
            return;
        }

        // Add a refund-specific note alongside AutoNotes so the order
        // linkage stays visible in the subscription timeline.
        if (function_exists('arraysubs_add_subscription_note')) {
            arraysubs_add_subscription_note(
                $subscription_id,
                sprintf(
                    /* translators: %d: order ID */
                    __('Cancellation triggered by full refund of order #%d.', 'arraysubs'),
                    $order_id
                ),
                'system',
                0,
                [],
                'subscription'
            );
        }
    }

    /**
     * Log refund to subscription meta
     *
     * @param int    $subscription_id Subscription ID
     * @param int    $order_id        Order ID
     * @param int    $refund_id       Refund ID
     * @param float  $amount          Refund amount
     * @param string $reason          Refund reason
     */
    private function logRefund(int $subscription_id, int $order_id, int $refund_id, float $amount, string $reason): void
    {
        $refund_log = get_post_meta($subscription_id, '_refund_history', true) ?: [];

        $refund_log[] = [
            'date' => current_time('mysql', true),
            'order_id' => $order_id,
            'refund_id' => $refund_id,
            'amount' => $amount,
            'reason' => $reason,
        ];

        update_post_meta($subscription_id, '_refund_history', $refund_log);

        // Update total refunded amount
        $total_refunded = floatval(get_post_meta($subscription_id, '_total_refunded', true));
        update_post_meta($subscription_id, '_total_refunded', $total_refunded + $amount);
    }

    /**
     * Find subscription by parent order ID
     *
     * @param int $order_id Parent order ID
     * @return int|null Subscription ID or null
     */
    private function findSubscriptionByParentOrder(int $order_id): ?int
    {
        // Search for subscription with this as parent order
        $subscriptions = get_posts([
            'post_type' => 'arraysubs_data',
            'post_status' => 'any',
            'meta_query' => [
                'relation' => 'OR',
                [
                    'key' => '_original_order_id',
                    'value' => $order_id,
                    'type' => 'NUMERIC',
                ],
                [
                    'key' => '_pending_renewal_order_id',
                    'value' => $order_id,
                    'type' => 'NUMERIC',
                ],
            ],
            'posts_per_page' => 1,
            'fields' => 'ids',
        ]);

        return !empty($subscriptions) ? $subscriptions[0] : null;
    }

    /**
     * Display subscription refund info on order edit screen
     *
     * @param \WC_Order $order Order object
     */
    public function displaySubscriptionRefundInfo(\WC_Order $order): void
    {
        $linked = arraysubs_get_subscription_ids_for_order($order);
        $subscription_id = !empty($linked) ? $linked[0] : 0;
        if (!$subscription_id) {
            return;
        }

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

?>
        <div class="arraysubs-order-subscription-info">
            <p>
                <strong><?php esc_html_e('ArraySubs Subscription:', 'arraysubs'); ?></strong>
                <a href="<?php echo esc_url(admin_url('admin.php?page=arraysubs-mainadmin#/subscriptions/detail/' . $subscription_id)); ?>">
                    #<?php echo esc_html($subscription_id); ?>
                </a>
                (<?php echo esc_html($subscription->post_status); ?>)
            </p>
            <?php
            $total_refunded = get_post_meta($subscription_id, '_total_refunded', true);
            if ($total_refunded > 0) {
            ?>
                <p>
                    <strong><?php esc_html_e('Total Refunded:', 'arraysubs'); ?></strong>
                    <?php echo wp_kses_post(wc_price($total_refunded)); ?>
                </p>
            <?php
            }
            ?>
        </div>
    <?php
    }

    /**
     * Add prorated refund action to subscription admin
     *
     * @param int      $subscription_id Subscription ID
     * @param \WP_Post $subscription    Subscription post object
     */
    public function addProratedRefundAction(int $subscription_id, \WP_Post $subscription): void
    {
        // Only show for active subscriptions with renewals
        $last_payment_date = get_post_meta($subscription_id, '_last_payment_date', true);
        if (empty($last_payment_date)) {
            return;
        }

        // Calculate prorated refund preview
        $proration = \ArraySubs\Features\PlanSwitching\Services\ProrationCalculator::calculateProrationRefund($subscription_id);

        if (!$proration['success'] || $proration['refund_amount'] <= 0) {
            return;
        }

    ?>
        <div class="arraysubs-admin-action arraysubs-prorated-refund-action">
            <button
                type="button"
                class="button button-secondary"
                data-subscription-id="<?php echo esc_attr($subscription_id); ?>"
                data-refund-amount="<?php echo esc_attr($proration['refund_amount']); ?>"
                data-days-unused="<?php echo esc_attr($proration['days_unused']); ?>">
                <?php
                printf(
                    /* translators: %s: prorated refund amount */
                    esc_html__('Issue Prorated Refund (%s)', 'arraysubs'),
                    wp_kses_post($proration['formatted']['refund'])
                );
                ?>
            </button>
            <span class="description">
                <?php
                printf(
                    /* translators: %s: days unused */
                    esc_html__('%s days unused in current cycle', 'arraysubs'),
                    esc_html(\ArraySubs\Features\PlanSwitching\Services\ProrationCalculator::formatDayCount((float) $proration['days_unused']))
                );
                ?>
            </span>
        </div>
<?php
    }
}
