<?php

/**
 * Pause Manager Service
 *
 * Handles all pause/resume subscription operations including validation,
 * execution, status changes, and auto-resume scheduling.
 *
 * @package ArraySubs\Features\SkipRenewal\Services
 * @since 1.0.0
 */

namespace ArraySubs\Features\SkipRenewal\Services;

defined('ABSPATH') || exit;

use ArraySubs\Supports\ActionScheduler;
use ArraySubs\Supports\Config;
use ArraySubs\Features\RecurringBilling\Services\RenewalScheduler;

/**
 * Pause Manager class
 */
class PauseManager
{
    /**
     * Check if a subscription can be paused
     *
     * Validates against settings, pause limits, and cooldown periods.
     *
     * @param int $subscription_id Subscription post ID
     * @return array {
     *     @type bool   $can_pause         Whether pause is allowed
     *     @type string $reason            Reason if not allowed (translated)
     *     @type int    $max_duration_days Maximum pause duration allowed
     *     @type int    $remaining_pauses  Number of pauses remaining
     * }
     */
    public function canPause(int $subscription_id): array
    {
        $subscription = get_post($subscription_id);

        if (!$subscription instanceof \WP_Post || $subscription->post_type !== 'arraysubs_data') {
            return [
                'can_pause'          => false,
                'reason'             => __('Invalid subscription.', 'arraysubs'),
                'max_duration_days'  => 0,
                'remaining_pauses'   => 0,
            ];
        }

        // Check if pause feature is enabled
        $settings = $this->getPauseSettings();
        if (!$settings['enabled']) {
            return [
                'can_pause'          => false,
                'reason'             => __('Pause subscription feature is disabled.', 'arraysubs'),
                'max_duration_days'  => 0,
                'remaining_pauses'   => 0,
            ];
        }

        // Check subscription status - only active subscriptions can be paused
        $allowed_statuses = ['arraysubs-active', 'arraysubs-trial'];
        if (!in_array($subscription->post_status, $allowed_statuses, true)) {
            return [
                'can_pause'          => false,
                'reason'             => __('Only active subscriptions can be paused.', 'arraysubs'),
                'max_duration_days'  => 0,
                'remaining_pauses'   => 0,
            ];
        }

        // Check pause count limit
        $pause_count = (int) get_post_meta($subscription_id, '_pause_count', true);
        $max_pauses = $settings['max_pauses_per_subscription'];
        $remaining_pauses = max(0, $max_pauses - $pause_count);

        if ($remaining_pauses <= 0) {
            return [
                'can_pause'          => false,
                'reason'             => sprintf(
                    /* translators: %d: maximum number of pauses allowed */
                    __('Maximum pause limit of %d reached for this subscription.', 'arraysubs'),
                    $max_pauses
                ),
                'max_duration_days'  => 0,
                'remaining_pauses'   => 0,
            ];
        }

        // Check cooldown between pauses
        $last_pause_date = get_post_meta($subscription_id, '_last_pause_date', true);
        if (!empty($last_pause_date)) {
            $days_since_last_pause = $this->getDaysSinceDate($last_pause_date);
            if ($days_since_last_pause !== false && $days_since_last_pause < $settings['min_days_between_pauses']) {
                $days_remaining = $settings['min_days_between_pauses'] - $days_since_last_pause;
                return [
                    'can_pause'          => false,
                    'reason'             => sprintf(
                        /* translators: %d: days remaining until pause is allowed */
                        __('Cannot pause again for %d more day(s).', 'arraysubs'),
                        $days_remaining
                    ),
                    'max_duration_days'  => 0,
                    'remaining_pauses'   => $remaining_pauses,
                ];
            }
        }

        return [
            'can_pause'          => true,
            'reason'             => '',
            'max_duration_days'  => $settings['max_duration_days'],
            'remaining_pauses'   => $remaining_pauses,
        ];
    }

    /**
     * Pause a subscription
     *
     * @param int    $subscription_id Subscription post ID
     * @param int    $duration_days   Pause duration in days
     * @param int    $user_id        User ID who requested the pause (0 = system)
     * @param string $reason         Optional reason for pausing
     * @return bool Whether pause was successful
     */
    public function pause(int $subscription_id, int $duration_days, int $user_id = 0, string $reason = ''): bool
    {
        // Validate can pause
        $can_pause = $this->canPause($subscription_id);
        if (!$can_pause['can_pause']) {
            return false;
        }

        // Validate duration
        $settings = $this->getPauseSettings();
        $duration_days = min(max(1, $duration_days), $settings['max_duration_days']);

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

        // Store current status for restoration later
        $current_status = $subscription->post_status;

        // Calculate dates
        $pause_start = current_time('mysql', true);
        $pause_end_timestamp = time() + ($duration_days * DAY_IN_SECONDS);
        $pause_end = gmdate('Y-m-d H:i:s', $pause_end_timestamp);

        // Get current next payment date and end date to extend them
        $next_payment_date = get_post_meta($subscription_id, '_next_payment_date', true);
        $end_date = get_post_meta($subscription_id, '_end_date', true);

        // Update pause meta fields
        update_post_meta($subscription_id, '_pause_start_date', $pause_start);
        update_post_meta($subscription_id, '_pause_end_date', $pause_end);
        update_post_meta($subscription_id, '_pause_duration_days', $duration_days);
        update_post_meta($subscription_id, '_pause_reason', sanitize_text_field($reason));
        update_post_meta($subscription_id, '_pause_requested_by', $user_id);
        update_post_meta($subscription_id, '_pre_pause_status', $current_status);

        if (!empty($next_payment_date)) {
            update_post_meta($subscription_id, '_pause_original_next_payment_date', $next_payment_date);
            update_post_meta($subscription_id, '_next_payment_date', $this->addDaysToDate($next_payment_date, $duration_days));
        }

        if (!empty($end_date)) {
            update_post_meta($subscription_id, '_pause_original_end_date', $end_date);
            update_post_meta($subscription_id, '_end_date', $this->addDaysToDate($end_date, $duration_days));
        }

        // Increment pause count
        $pause_count = (int) get_post_meta($subscription_id, '_pause_count', true);
        update_post_meta($subscription_id, '_pause_count', $pause_count + 1);
        update_post_meta($subscription_id, '_last_pause_date', $pause_start);

        // Update subscription status to On-Hold
        wp_update_post([
            'ID'          => $subscription_id,
            'post_status' => 'arraysubs-on-hold',
        ]);

        // Add to pause history
        $this->addToPauseHistory($subscription_id, [
            'action'           => 'pause',
            'duration_days'    => $duration_days,
            'start_date'       => $pause_start,
            'end_date'         => $pause_end,
            'reason'           => $reason,
            'previous_status'  => $current_status,
            'requested_by'     => $user_id,
            'requested_at'     => $pause_start,
        ]);

        // Schedule auto-resume action
        $this->scheduleAutoResume($subscription_id, $pause_end_timestamp);

        // Unschedule any pending renewal/invoice actions during pause.
        RenewalScheduler::unschedule($subscription_id);

        // Fire action for integrations (notes, emails, etc.)
        do_action('arraysubs_subscription_paused', $subscription_id, $duration_days, $user_id, $reason);
        return true;
    }

    /**
     * Resume a paused subscription
     *
     * @param int  $subscription_id Subscription post ID
     * @param int  $user_id        User ID who is resuming (0 = system)
     * @param bool $is_auto        Whether this is an auto-resume (scheduled)
     * @return bool Whether resume was successful
     */
    public function resume(int $subscription_id, int $user_id = 0, bool $is_auto = false): bool
    {
        $subscription = get_post($subscription_id);
        if (!$subscription instanceof \WP_Post) {
            return false;
        }

        // Check if currently paused (on-hold)
        if ($subscription->post_status !== 'arraysubs-on-hold') {
            return false;
        }

        // Get pause details
        $pause_start = get_post_meta($subscription_id, '_pause_start_date', true);
        $pause_end = get_post_meta($subscription_id, '_pause_end_date', true);
        $pre_pause_status = get_post_meta($subscription_id, '_pre_pause_status', true);
        $pause_duration = (int) get_post_meta($subscription_id, '_pause_duration_days', true);

        // Calculate actual pause duration
        $actual_pause_days = $this->calculateActualPauseDays($pause_start);

        // Calculate status to restore (default to active if not stored)
        $restore_status = !empty($pre_pause_status) ? $pre_pause_status : 'arraysubs-active';

        $this->restorePausedTimelineDate(
            $subscription_id,
            '_next_payment_date',
            '_pause_original_next_payment_date',
            $actual_pause_days
        );

        $this->restorePausedTimelineDate(
            $subscription_id,
            '_end_date',
            '_pause_original_end_date',
            $actual_pause_days
        );

        // Update subscription status
        wp_update_post([
            'ID'          => $subscription_id,
            'post_status' => $restore_status,
        ]);

        // Clear pause meta (keep history)
        $this->clearPauseMeta($subscription_id);

        // Unschedule any auto-resume actions
        ActionScheduler::unscheduleAll(
            ActionScheduler::HOOK_RESUME_SUBSCRIPTION,
            [$subscription_id],
            ActionScheduler::GROUP_STATUS
        );

        $this->scheduleNextRenewal($subscription_id);

        // Add to pause history
        $this->addToPauseHistory($subscription_id, [
            'action'         => 'resume',
            'is_auto'        => $is_auto,
            'paused_days'    => $actual_pause_days,
            'resumed_to'     => $restore_status,
            'requested_by'   => $user_id,
            'requested_at'   => current_time('mysql', true),
        ]);

        // Fire action for integrations
        do_action('arraysubs_subscription_resumed', $subscription_id, $user_id, $is_auto, [
            'paused_days'  => $actual_pause_days,
            'restored_to'  => $restore_status,
        ]);

        return true;
    }

    /**
     * Get the current pause status for a subscription
     *
     * @param int $subscription_id Subscription post ID
     * @return array Pause status information
     */
    public function getPauseStatus(int $subscription_id): array
    {
        $subscription = get_post($subscription_id);
        $is_paused = ($subscription instanceof \WP_Post && $subscription->post_status === 'arraysubs-on-hold');

        $pause_start = get_post_meta($subscription_id, '_pause_start_date', true);
        $pause_end = get_post_meta($subscription_id, '_pause_end_date', true);
        $pause_duration = (int) get_post_meta($subscription_id, '_pause_duration_days', true);
        $pause_count = (int) get_post_meta($subscription_id, '_pause_count', true);

        $days_paused = 0;
        $days_remaining = 0;

        if ($is_paused && !empty($pause_start)) {
            $days_paused = $this->calculateActualPauseDays($pause_start);
            if (!empty($pause_end)) {
                $days_remaining = max(0, $this->getDaysUntilDate($pause_end));
            }
        }

        return [
            'is_paused'        => $is_paused,
            'start_date'       => $pause_start,
            'end_date'         => $pause_end,
            'duration_days'    => $pause_duration,
            'days_paused'      => $days_paused,
            'days_remaining'   => $days_remaining,
            'pause_count'      => $pause_count,
            'reason'           => get_post_meta($subscription_id, '_pause_reason', true),
            'pre_pause_status' => get_post_meta($subscription_id, '_pre_pause_status', true),
            'requested_by'     => (int) get_post_meta($subscription_id, '_pause_requested_by', true),
        ];
    }

    /**
     * Get time since last pause
     *
     * @param int $subscription_id Subscription post ID
     * @return int|null Days since last pause or null if never paused
     */
    public function getTimeSinceLastPause(int $subscription_id): ?int
    {
        $last_pause = get_post_meta($subscription_id, '_last_pause_date', true);
        if (empty($last_pause)) {
            return null;
        }

        return $this->getDaysSinceDate($last_pause);
    }

    /**
     * Get pause count for a subscription
     *
     * @param int $subscription_id Subscription post ID
     * @return int Number of times subscription has been paused
     */
    public function getPauseCount(int $subscription_id): int
    {
        return (int) get_post_meta($subscription_id, '_pause_count', true);
    }

    /**
     * Get pause settings
     *
     * @return array Settings with defaults
     */
    private function getPauseSettings(): array
    {
        $settings = Config::get('settings.pause_subscription', []);

        return wp_parse_args($settings, [
            'enabled'                     => true,
            'max_duration_days'           => 30,
            'max_pauses_per_subscription' => 2,
            'min_days_between_pauses'     => 30,
            'customer_can_pause'          => true,
            'require_reason'              => false,
        ]);
    }

    /**
     * Schedule auto-resume action
     *
     * @param int $subscription_id Subscription post ID
     * @param int $timestamp       Unix timestamp when to resume
     * @return void
     */
    private function scheduleAutoResume(int $subscription_id, int $timestamp): void
    {
        ActionScheduler::scheduleSingle(
            ActionScheduler::HOOK_RESUME_SUBSCRIPTION,
            $timestamp,
            [$subscription_id],
            ActionScheduler::GROUP_STATUS
        );
    }

    /**
     * Calculate actual pause days from start date
     *
     * @param string $start_date Pause start date
     * @return int Actual days paused
     */
    private function calculateActualPauseDays(string $start_date): int
    {
        if (empty($start_date)) {
            return 0;
        }

        $start_timestamp = strtotime($start_date);
        $now = time();

        return max(0, (int) floor(($now - $start_timestamp) / DAY_IN_SECONDS));
    }

    /**
     * Get days since a given date
     *
     * @param string $date Date in MySQL format
     * @return int|false Days since date or false if invalid
     */
    private function getDaysSinceDate(string $date)
    {
        $timestamp = strtotime($date);
        if (!$timestamp) {
            return false;
        }

        $now = time();
        $diff = $now - $timestamp;

        return max(0, (int) floor($diff / DAY_IN_SECONDS));
    }

    /**
     * Get days until a given date
     *
     * @param string $date Date in MySQL format
     * @return int|false Days until date or false if invalid
     */
    private function getDaysUntilDate(string $date)
    {
        $timestamp = strtotime($date);
        if (!$timestamp) {
            return false;
        }

        $now = time();
        $diff = $timestamp - $now;

        return max(0, (int) floor($diff / DAY_IN_SECONDS));
    }

    /**
     * Restore a billing timeline date from its pre-pause value plus actual paused days.
     *
     * @param int    $subscription_id Subscription ID.
     * @param string $target_meta     Target date meta key.
     * @param string $original_meta   Original date meta key captured before pause.
     * @param int    $days            Actual paused days.
     * @return void
     */
    private function restorePausedTimelineDate(int $subscription_id, string $target_meta, string $original_meta, int $days): void
    {
        $original_date = get_post_meta($subscription_id, $original_meta, true);
        if (empty($original_date)) {
            return;
        }

        update_post_meta($subscription_id, $target_meta, $this->addDaysToDate($original_date, $days));
    }

    /**
     * Add whole days to a stored UTC MySQL date.
     *
     * @param string $date Stored date.
     * @param int    $days Days to add.
     * @return string Shifted date, or original date if parsing fails.
     */
    private function addDaysToDate(string $date, int $days): string
    {
        $timestamp = strtotime($date);
        if (!$timestamp) {
            return $date;
        }

        return gmdate('Y-m-d H:i:s', strtotime("+{$days} days", $timestamp));
    }

    /**
     * Extend next payment date by pause duration
     *
     * @param int $subscription_id Subscription post ID
     * @param int $days            Days to extend
     * @return void
     */
    private function extendNextPaymentDate(int $subscription_id, int $days): void
    {
        $next_payment = get_post_meta($subscription_id, '_next_payment_date', true);
        if (empty($next_payment)) {
            return;
        }

        $new_date = gmdate('Y-m-d H:i:s', strtotime("+{$days} days", strtotime($next_payment)));
        update_post_meta($subscription_id, '_next_payment_date', $new_date);
    }

    /**
     * Schedule renewal processing after resume.
     *
     * @param int $subscription_id Subscription post ID
     * @return void
     */
    private function scheduleNextRenewal(int $subscription_id): void
    {
        $next_payment = get_post_meta($subscription_id, '_next_payment_date', true);
        if (empty($next_payment)) {
            return;
        }

        $timestamp = strtotime($next_payment);
        if (!$timestamp) {
            return;
        }

        if ($timestamp <= time()) {
            $timestamp = time() + MINUTE_IN_SECONDS;
        }

        RenewalScheduler::unschedule($subscription_id);
        RenewalScheduler::schedule($subscription_id, $timestamp);
    }

    /**
     * Extend subscription end date by pause duration
     *
     * @param int $subscription_id Subscription post ID
     * @param int $days            Days to extend
     * @return void
     */
    private function extendEndDate(int $subscription_id, int $days): void
    {
        $end_date = get_post_meta($subscription_id, '_end_date', true);
        if (empty($end_date)) {
            return;
        }

        $new_date = gmdate('Y-m-d H:i:s', strtotime("+{$days} days", strtotime($end_date)));
        update_post_meta($subscription_id, '_end_date', $new_date);
    }

    /**
     * Add entry to pause history
     *
     * @param int   $subscription_id Subscription post ID
     * @param array $entry           History entry data
     * @return void
     */
    private function addToPauseHistory(int $subscription_id, array $entry): void
    {
        $history = get_post_meta($subscription_id, '_pause_history', true);
        $history = !empty($history) ? json_decode($history, true) : [];

        if (!is_array($history)) {
            $history = [];
        }

        $history[] = $entry;

        // Keep only last 50 entries
        if (count($history) > 50) {
            $history = array_slice($history, -50);
        }

        update_post_meta($subscription_id, '_pause_history', wp_json_encode($history));
    }

    /**
     * Clear pause meta fields (but keep history and count)
     *
     * @param int $subscription_id Subscription post ID
     * @return void
     */
    private function clearPauseMeta(int $subscription_id): void
    {
        delete_post_meta($subscription_id, '_pause_start_date');
        delete_post_meta($subscription_id, '_pause_end_date');
        delete_post_meta($subscription_id, '_pause_duration_days');
        delete_post_meta($subscription_id, '_pause_reason');
        delete_post_meta($subscription_id, '_pause_requested_by');
        delete_post_meta($subscription_id, '_pre_pause_status');
        delete_post_meta($subscription_id, '_pause_original_next_payment_date');
        delete_post_meta($subscription_id, '_pause_original_end_date');
    }
}
