<?php

/**
 * Expiration Checker
 *
 * Handles checking and processing subscription expirations.
 *
 * @package ArraySubs\Features\RecurringBilling\Services
 * @since 1.0.0
 */

namespace ArraySubs\Features\RecurringBilling\Services;

defined('ABSPATH') || exit;

use ArraySubs\Features\RecurringBilling\Services\RenewalScheduler;

/**
 * Expiration checker service
 */
class ExpirationChecker
{
    /**
     * Check if subscription should expire
     *
     * @param int $subscription_id Subscription ID
     * @return bool True if should expire
     */
    public function shouldExpire(int $subscription_id): bool
    {
        // Lifetime subscriptions do not expire by payment count
        if (arraysubs_is_lifetime_subscription($subscription_id)) {
            return false;
        }

        // Fixed-end-date expiration (date-based, checked first)
        if ($this->shouldExpireByEndDate($subscription_id)) {
            return true;
        }

        // Payment-count expiration (length-based)
        $length = (int) get_post_meta($subscription_id, '_subscription_length', true);

        if ($length === 0) {
            return false; // Never expires
        }

        // Count completed payments
        $payments_made = $this->countCompletedPayments($subscription_id);

        // Should expire if payments made >= length
        return $payments_made >= $length;
    }

    /**
     * Check if subscription should expire based on a fixed end date.
     *
     * @param int $subscription_id Subscription ID.
     * @return bool True if the fixed end date has passed.
     */
    public function shouldExpireByEndDate(int $subscription_id): bool
    {
        $end_date = get_post_meta($subscription_id, '_end_date', true);

        if (empty($end_date) || '0000-00-00 00:00:00' === $end_date) {
            return false;
        }

        // Only apply to subscriptions that have the fixed-end-date flag.
        // Active subscriptions without the flag have _end_date set at
        // cancellation/expiration time, not as a future target.
        $fixed_enabled = get_post_meta($subscription_id, '_arraysubs_fixed_end_date_enabled', true);

        if ('yes' !== $fixed_enabled) {
            return false;
        }

        $now_utc = current_time('mysql', true);

        return $now_utc >= $end_date;
    }

    /**
     * Count completed payments
     *
     * @param int $subscription_id Subscription ID
     * @return int Number of completed payments
     */
    private function countCompletedPayments(int $subscription_id): int
    {
        // Get all orders for this subscription
        $order_ids = arraysubs_get_subscription_orders($subscription_id);

        if (empty($order_ids)) {
            return 0;
        }

        $completed = 0;

        foreach ($order_ids as $order_id) {
            $order = wc_get_order($order_id);

            if (!$order) {
                continue;
            }

            // Count completed/processing orders
            if ($order->has_status(['completed', 'processing'])) {
                ++$completed;
            }
        }

        return $completed;
    }

    /**
     * Expire a subscription
     *
     * @param int $subscription_id Subscription ID
     * @return bool True on success
     */
    public function expire(int $subscription_id): bool
    {
        /**
         * Filter whether a subscription should be expired.
         *
         * Returning false prevents the subscription from being expired.
         * Used by pro features like Fixed Period Membership to renew into the next period.
         *
         * @since 1.0.0
         * @param bool $should_expire    Whether the subscription should expire. Default true.
         * @param int  $subscription_id Subscription ID.
         */
        if (!apply_filters('arraysubs_should_expire_subscription', true, $subscription_id)) {
            return false;
        }

        // Update status to expired
        $result = wp_update_post([
            'ID' => $subscription_id,
            'post_status' => 'arraysubs-expired',
        ]);

        if (is_wp_error($result)) {
            return false;
        }

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

        // Auto-downgrade may have already reactivated the subscription during the
        // transition callback. In that case, keep the newly scheduled billing state.
        if ($subscription->post_status !== 'arraysubs-expired') {
            return true;
        }

        update_post_meta($subscription_id, '_end_date', $this->getExpirationEndDate($subscription_id));

        // Unschedule any future renewals
        RenewalScheduler::unschedule($subscription_id);

        // Trigger action for email notifications
        do_action('arraysubs_data_expired', $subscription_id);

        return true;
    }

    /**
     * Get the end date that should remain on an expired subscription.
     *
     * @param int $subscription_id Subscription ID.
     * @return string UTC MySQL date.
     */
    private function getExpirationEndDate(int $subscription_id): string
    {
        $configured_end = (string) get_post_meta($subscription_id, '_end_date', true);
        $fixed_enabled  = get_post_meta($subscription_id, '_arraysubs_fixed_end_date_enabled', true);

        if (
            'yes' === $fixed_enabled
            && $configured_end !== ''
            && '0000-00-00 00:00:00' !== $configured_end
            && false !== strtotime($configured_end)
        ) {
            return $configured_end;
        }

        return current_time('mysql', true);
    }

    /**
     * Check all active subscriptions for expiration
     *
     * @return int Number of subscriptions expired
     */
    public function checkAllSubscriptions(): int
    {
        // 1) Payment-count based subscriptions (length > 0, non-lifetime)
        $length_args = [
            'post_type'      => 'arraysubs_data',
            'post_status'    => 'arraysubs-active',
            'posts_per_page' => -1,
            'fields'         => 'ids',
            'meta_query'     => [
                [
                    'key'     => '_subscription_length',
                    'value'   => 0,
                    'compare' => '>',
                    'type'    => 'NUMERIC',
                ],
                [
                    'key'     => '_billing_period',
                    'value'   => 'lifetime',
                    'compare' => '!=',
                ],
            ],
        ];

        $length_ids = get_posts($length_args);

        // 2) Fixed-end-date subscriptions (have _arraysubs_fixed_end_date_enabled = yes)
        $fixed_args = [
            'post_type'      => 'arraysubs_data',
            'post_status'    => ['arraysubs-active', 'arraysubs-trial', 'arraysubs-on-hold'],
            'posts_per_page' => -1,
            'fields'         => 'ids',
            'meta_query'     => [
                [
                    'key'     => '_arraysubs_fixed_end_date_enabled',
                    'value'   => 'yes',
                    'compare' => '=',
                ],
                [
                    'key'     => '_end_date',
                    'value'   => '',
                    'compare' => '!=',
                ],
            ],
        ];

        $fixed_ids = get_posts($fixed_args);

        // Merge and deduplicate
        $subscription_ids = array_unique(array_merge($length_ids, $fixed_ids));
        $expired_count    = 0;

        foreach ($subscription_ids as $subscription_id) {
            if ($this->shouldExpire($subscription_id)) {
                if ($this->expire($subscription_id)) {
                    ++$expired_count;
                }
            }
        }

        return $expired_count;
    }
}
