<?php

/**
 * Subscription Manager
 * 
 * Core business logic for subscription management
 *
 * @package WooNooW\Modules\Subscription
 */

namespace WooNooW\Modules\Subscription;

if (!defined('ABSPATH')) exit;

use WooNooW\Core\ModuleRegistry;

class SubscriptionManager
{

    /** @var string Subscriptions table name */
    private static $table_subscriptions;

    /** @var string Subscription orders table name */
    private static $table_subscription_orders;

    /**
     * Initialize the manager
     */
    public static function init()
    {
        global $wpdb;
        self::$table_subscriptions = $wpdb->prefix . 'woonoow_subscriptions';
        self::$table_subscription_orders = $wpdb->prefix . 'woonoow_subscription_orders';
    }

    /**
     * Create database tables
     */
    public static function create_tables()
    {
        global $wpdb;

        $charset_collate = $wpdb->get_charset_collate();
        $table_subscriptions = $wpdb->prefix . 'woonoow_subscriptions';
        $table_subscription_orders = $wpdb->prefix . 'woonoow_subscription_orders';

        $sql_subscriptions = "CREATE TABLE $table_subscriptions (
            id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
            user_id BIGINT UNSIGNED NOT NULL,
            order_id BIGINT UNSIGNED NOT NULL,
            product_id BIGINT UNSIGNED NOT NULL,
            variation_id BIGINT UNSIGNED DEFAULT NULL,
            status ENUM('pending', 'active', 'on-hold', 'cancelled', 'expired', 'pending-cancel') DEFAULT 'pending',
            billing_period ENUM('day', 'week', 'month', 'year') NOT NULL,
            billing_interval INT UNSIGNED DEFAULT 1,
            recurring_amount DECIMAL(12,4) DEFAULT 0,
            start_date DATETIME NOT NULL,
            trial_end_date DATETIME DEFAULT NULL,
            next_payment_date DATETIME DEFAULT NULL,
            end_date DATETIME DEFAULT NULL,
            last_payment_date DATETIME DEFAULT NULL,
            payment_method VARCHAR(100) DEFAULT NULL,
            payment_meta LONGTEXT,
            cancel_reason TEXT DEFAULT NULL,
            pause_count INT UNSIGNED DEFAULT 0,
            failed_payment_count INT UNSIGNED DEFAULT 0,
            reminder_sent_at DATETIME DEFAULT NULL,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            INDEX idx_user_id (user_id),
            INDEX idx_order_id (order_id),
            INDEX idx_product_id (product_id),
            INDEX idx_status (status),
            INDEX idx_next_payment (next_payment_date)
        ) $charset_collate;";

        $sql_orders = "CREATE TABLE $table_subscription_orders (
            id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
            subscription_id BIGINT UNSIGNED NOT NULL,
            order_id BIGINT UNSIGNED NOT NULL,
            order_type ENUM('parent', 'renewal', 'switch', 'resubscribe') DEFAULT 'renewal',
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            INDEX idx_subscription (subscription_id),
            INDEX idx_order (order_id)
        ) $charset_collate;";

        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        dbDelta($sql_subscriptions);
        dbDelta($sql_orders);
    }

    /**
     * Create subscription from order item
     * 
     * @param \WC_Order $order
     * @param \WC_Order_Item_Product $item
     * @return int|false Subscription ID or false on failure
     */
    public static function create_from_order($order, $item)
    {
        global $wpdb;

        $product_id = $item->get_product_id();
        $variation_id = $item->get_variation_id();
        $user_id = $order->get_user_id();

        if (!$user_id) {
            // Guest orders not supported for subscriptions
            return false;
        }

        // Get subscription settings from product
        $billing_period = get_post_meta($product_id, '_woonoow_subscription_period', true) ?: 'month';
        $billing_interval = absint(get_post_meta($product_id, '_woonoow_subscription_interval', true)) ?: 1;
        $trial_days = absint(get_post_meta($product_id, '_woonoow_subscription_trial_days', true));
        $subscription_length = absint(get_post_meta($product_id, '_woonoow_subscription_length', true));

        // Calculate dates
        $now = current_time('mysql');
        $start_date = $now;
        $trial_end_date = null;

        if ($trial_days > 0) {
            $trial_end_date = date('Y-m-d H:i:s', strtotime($now . " + $trial_days days"));
            $next_payment_date = $trial_end_date;
        } else {
            $next_payment_date = self::calculate_next_payment_date($now, $billing_period, $billing_interval);
        }

        // Calculate end date if subscription has fixed length
        $end_date = null;
        if ($subscription_length > 0) {
            $end_date = self::calculate_end_date($start_date, $billing_period, $billing_interval, $subscription_length);
        }

        // Get recurring amount (product price)
        $product = $item->get_product();
        $recurring_amount = $product ? $product->get_price() : $item->get_total();

        // Get payment method
        $payment_method = $order->get_payment_method();
        $payment_meta = json_encode([
            'method_title' => $order->get_payment_method_title(),
            'customer_id' => $order->get_customer_id(),
        ]);

        // Insert subscription
        $inserted = $wpdb->insert(
            self::$table_subscriptions,
            [
                'user_id' => $user_id,
                'order_id' => $order->get_id(),
                'product_id' => $product_id,
                'variation_id' => $variation_id ?: null,
                'status' => 'active',
                'billing_period' => $billing_period,
                'billing_interval' => $billing_interval,
                'recurring_amount' => $recurring_amount,
                'start_date' => $start_date,
                'trial_end_date' => $trial_end_date,
                'next_payment_date' => $next_payment_date,
                'end_date' => $end_date,
                'last_payment_date' => $now,
                'payment_method' => $payment_method,
                'payment_meta' => $payment_meta,
            ],
            ['%d', '%d', '%d', '%d', '%s', '%s', '%d', '%f', '%s', '%s', '%s', '%s', '%s', '%s', '%s']
        );

        if (!$inserted) {
            return false;
        }

        $subscription_id = $wpdb->insert_id;

        // Link parent order to subscription
        $wpdb->insert(
            self::$table_subscription_orders,
            [
                'subscription_id' => $subscription_id,
                'order_id' => $order->get_id(),
                'order_type' => 'parent',
            ],
            ['%d', '%d', '%s']
        );

        // Trigger action
        do_action('woonoow/subscription/created', $subscription_id, $order, $item);

        return $subscription_id;
    }

    /**
     * Get subscription by ID
     * 
     * @param int $subscription_id
     * @return object|null
     */
    public static function get($subscription_id)
    {
        global $wpdb;

        return $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM " . self::$table_subscriptions . " WHERE id = %d",
            $subscription_id
        ));
    }

    /**
     * Get subscription by related order ID (parent or renewal)
     * 
     * @param int $order_id
     * @return object|null
     */
    public static function get_by_order_id($order_id)
    {
        global $wpdb;

        $table_subscriptions = $wpdb->prefix . 'woonoow_subscriptions';
        $table_subscription_orders = $wpdb->prefix . 'woonoow_subscription_orders';

        // Join subscriptions table to get full subscription data
        return $wpdb->get_row($wpdb->prepare(
            "SELECT s.* 
             FROM $table_subscriptions s
             JOIN $table_subscription_orders so ON s.id = so.subscription_id
             WHERE so.order_id = %d",
            $order_id
        ));
    }

    /**
     * Get subscriptions by user
     * 
     * @param int $user_id
     * @param array $args
     * @return array
     */
    public static function get_by_user($user_id, $args = [])
    {
        global $wpdb;

        $defaults = [
            'status' => null,
            'limit' => 20,
            'offset' => 0,
        ];
        $args = wp_parse_args($args, $defaults);

        $where = "WHERE user_id = %d";
        $params = [$user_id];

        if ($args['status']) {
            $where .= " AND status = %s";
            $params[] = $args['status'];
        }

        $sql = $wpdb->prepare(
            "SELECT * FROM " . self::$table_subscriptions . " $where ORDER BY created_at DESC LIMIT %d OFFSET %d",
            array_merge($params, [$args['limit'], $args['offset']])
        );

        return $wpdb->get_results($sql);
    }

    /**
     * Get all subscriptions (admin)
     * 
     * @param array $args
     * @return array
     */
    public static function get_all($args = [])
    {
        global $wpdb;

        $defaults = [
            'status' => null,
            'product_id' => null,
            'user_id' => null,
            'limit' => 20,
            'offset' => 0,
            'search' => null,
        ];
        $args = wp_parse_args($args, $defaults);

        $where = "WHERE 1=1";
        $params = [];

        if ($args['status']) {
            $where .= " AND status = %s";
            $params[] = $args['status'];
        }

        if ($args['product_id']) {
            $where .= " AND product_id = %d";
            $params[] = $args['product_id'];
        }

        if ($args['user_id']) {
            $where .= " AND user_id = %d";
            $params[] = $args['user_id'];
        }

        $order = "ORDER BY created_at DESC";
        $limit = "LIMIT " . intval($args['limit']) . " OFFSET " . intval($args['offset']);

        $sql = "SELECT * FROM " . self::$table_subscriptions . " $where $order $limit";

        if (!empty($params)) {
            $sql = $wpdb->prepare($sql, $params);
        }

        return $wpdb->get_results($sql);
    }

    /**
     * Count subscriptions
     * 
     * @param array $args
     * @return int
     */
    public static function count($args = [])
    {
        global $wpdb;

        $where = "WHERE 1=1";
        $params = [];

        if (!empty($args['status'])) {
            $where .= " AND status = %s";
            $params[] = $args['status'];
        }

        $sql = "SELECT COUNT(*) FROM " . self::$table_subscriptions . " $where";

        if (!empty($params)) {
            $sql = $wpdb->prepare($sql, $params);
        }

        return (int) $wpdb->get_var($sql);
    }

    /**
     * Update subscription status
     * 
     * @param int $subscription_id
     * @param string $status
     * @param string|null $reason
     * @return bool
     */
    public static function update_status($subscription_id, $status, $reason = null)
    {
        global $wpdb;

        $data = ['status' => $status];
        $format = ['%s'];

        if ($reason !== null) {
            $data['cancel_reason'] = $reason;
            $format[] = '%s';
        }

        $updated = $wpdb->update(
            self::$table_subscriptions,
            $data,
            ['id' => $subscription_id],
            $format,
            ['%d']
        );

        if ($updated !== false) {
            do_action('woonoow/subscription/status_changed', $subscription_id, $status, $reason);
        }

        return $updated !== false;
    }

    /**
     * Cancel subscription
     * 
     * @param int $subscription_id
     * @param string $reason
     * @param bool $immediate Force immediate cancellation
     * @return bool
     */
    public static function cancel($subscription_id, $reason = '', $immediate = false)
    {
        $subscription = self::get($subscription_id);
        if (!$subscription || in_array($subscription->status, ['cancelled', 'expired'])) {
            return false;
        }

        // Default to pending-cancel if there's time left
        $new_status = 'cancelled';
        $now = current_time('mysql');

        if (!$immediate && $subscription->next_payment_date && $subscription->next_payment_date > $now) {
            $new_status = 'pending-cancel';
        }

        $success = self::update_status($subscription_id, $new_status, $reason);

        if ($success) {
            if ($new_status === 'pending-cancel') {
                do_action('woonoow/subscription/pending_cancel', $subscription_id, $reason);
            } else {
                do_action('woonoow/subscription/cancelled', $subscription_id, $reason);
            }
        }

        return $success;
    }

    /**
     * Pause subscription
     * 
     * @param int $subscription_id
     * @return bool
     */
    public static function pause($subscription_id)
    {
        global $wpdb;

        $subscription = self::get($subscription_id);
        if (!$subscription || $subscription->status !== 'active') {
            return false;
        }

        // Check max pause count
        $settings = ModuleRegistry::get_settings('subscription');
        $max_pause = $settings['max_pause_count'] ?? 3;

        if ($max_pause > 0 && $subscription->pause_count >= $max_pause) {
            return false;
        }

        $updated = $wpdb->update(
            self::$table_subscriptions,
            [
                'status' => 'on-hold',
                'pause_count' => $subscription->pause_count + 1,
            ],
            ['id' => $subscription_id],
            ['%s', '%d'],
            ['%d']
        );

        if ($updated !== false) {
            do_action('woonoow/subscription/paused', $subscription_id);
        }

        return $updated !== false;
    }

    /**
     * Resume subscription
     * 
     * @param int $subscription_id
     * @return bool
     */
    public static function resume($subscription_id)
    {
        global $wpdb;

        $subscription = self::get($subscription_id);
        if (!$subscription || !in_array($subscription->status, ['on-hold', 'pending-cancel'])) {
            return false;
        }

        $update_data = ['status' => 'active'];
        $format = ['%s'];

        // Only recalculate payment date if resuming from on-hold
        if ($subscription->status === 'on-hold') {
            // Recalculate next payment date from now
            $next_payment = self::calculate_next_payment_date(
                current_time('mysql'),
                $subscription->billing_period,
                $subscription->billing_interval
            );
            $update_data['next_payment_date'] = $next_payment;
            $format[] = '%s';
        }

        $updated = $wpdb->update(
            self::$table_subscriptions,
            $update_data,
            ['id' => $subscription_id],
            $format,
            ['%d']
        );

        if ($updated !== false) {
            do_action('woonoow/subscription/resumed', $subscription_id);
        }

        return $updated !== false;
    }

    /**
     * Process renewal for a subscription
     * 
     * @param int $subscription_id
     * @return bool
     */
    /**
     * Process renewal for a subscription
     * 
     * @param int $subscription_id
     * @return bool
     */
    public static function renew($subscription_id)
    {
        global $wpdb;

        $subscription = self::get($subscription_id);
        if (!$subscription || !in_array($subscription->status, ['active', 'on-hold'])) {
            return false;
        }

        // Check for existing pending renewal order to prevent duplicates
        $existing_pending = $wpdb->get_row($wpdb->prepare(
            "SELECT so.order_id FROM " . self::$table_subscription_orders . " so
             JOIN {$wpdb->posts} p ON so.order_id = p.ID
             WHERE so.subscription_id = %d
             AND so.order_type = 'renewal'
             AND p.post_status IN ('wc-pending', 'pending', 'wc-on-hold', 'on-hold')",
            $subscription_id
        ));

        if ($existing_pending) {
            return ['success' => true, 'order_id' => (int) $existing_pending->order_id, 'status' => 'existing'];
        }

        // Create renewal order
        $renewal_order = self::create_renewal_order($subscription);
        if (!$renewal_order) {
            // Failed to create order
            self::handle_renewal_failure($subscription_id);
            return false;
        }

        // Process payment
        // Result can be: true (paid), false (failed), or 'manual' (waiting for payment)
        $payment_result = self::process_renewal_payment($subscription, $renewal_order);

        if ($payment_result === true) {
            self::handle_renewal_success($subscription_id, $renewal_order);
            return ['success' => true, 'order_id' => $renewal_order->get_id(), 'status' => 'complete'];
        } elseif ($payment_result === 'manual') {
            // Manual payment required

            // CHECK: Is this an early renewal? (Next payment date is in future)
            $now = current_time('mysql');
            $is_early_renewal = $subscription->next_payment_date && $subscription->next_payment_date > $now;

            if ($is_early_renewal) {
                // Early renewal: Keep active, just waiting for payment
                return ['success' => true, 'order_id' => $renewal_order->get_id(), 'status' => 'manual'];
            }

            // Normal/Overdue renewal: Set to on-hold
            self::update_status($subscription_id, 'on-hold', 'awaiting_payment');
            return ['success' => true, 'order_id' => $renewal_order->get_id(), 'status' => 'manual'];
        } else {
            // Auto-debit failed
            self::handle_renewal_failure($subscription_id);
            return false;
        }
    }

    /**
     * Create a renewal order
     * 
     * @param object $subscription
     * @return \WC_Order|false
     */
    private static function create_renewal_order($subscription)
    {
        global $wpdb;

        // Get original order
        $parent_order = wc_get_order($subscription->order_id);
        if (!$parent_order) {
            return false;
        }

        // Create new order
        $renewal_order = wc_create_order([
            'customer_id' => $subscription->user_id,
            'status' => 'pending',
            'parent' => $subscription->order_id,
        ]);

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

        // Add product
        $product = wc_get_product($subscription->variation_id ?: $subscription->product_id);
        if ($product) {
            $renewal_order->add_product($product, 1, [
                'total' => $subscription->recurring_amount,
                'subtotal' => $subscription->recurring_amount,
            ]);
        }

        // Copy billing/shipping from parent
        $renewal_order->set_address($parent_order->get_address('billing'), 'billing');
        $renewal_order->set_address($parent_order->get_address('shipping'), 'shipping');
        $renewal_order->set_payment_method($subscription->payment_method);

        // Calculate totals
        $renewal_order->calculate_totals();
        $renewal_order->save();

        // Link to subscription
        $wpdb->insert(
            self::$table_subscription_orders,
            [
                'subscription_id' => $subscription->id,
                'order_id' => $renewal_order->get_id(),
                'order_type' => 'renewal',
            ],
            ['%d', '%d', '%s']
        );

        return $renewal_order;
    }

    /**
     * Process payment for renewal order
     * 
     * @param object $subscription
     * @param \WC_Order $order
     * @return bool
     */
    /**
     * Process payment for renewal order
     * 
     * @param object $subscription
     * @param \WC_Order $order
     * @return bool|string True if paid, false if failed, 'manual' if waiting
     */
    private static function process_renewal_payment($subscription, $order)
    {
        // Allow plugins to override payment processing completely
        // Return true/false/'manual' to bypass default logic
        $pre = apply_filters('woonoow_pre_process_subscription_payment', null, $subscription, $order);
        if ($pre !== null) {
            return $pre;
        }

        // Get payment gateway
        $gateways = WC()->payment_gateways()->get_available_payment_gateways();
        $gateway_id = $subscription->payment_method;

        if (!isset($gateways[$gateway_id])) {
            // Payment method not available - treat as failure so user can fix
            $order->update_status('failed', __('Payment method not available for renewal', 'woonoow'));
            return false;
        }

        $gateway = $gateways[$gateway_id];

        // 1. Try Auto-Debit if supported
        if (method_exists($gateway, 'process_subscription_renewal_payment')) {
            $result = $gateway->process_subscription_renewal_payment($order, $subscription);
            if (!is_wp_error($result) && $result) {
                return true;
            }
            // If explicit failure from auto-debit, return false (will trigger retry logic)
            return false;
        }

        // 2. Allow other plugins to handle auto-debit via filter (e.g. Stripe/PayPal adapters)
        $external_result = apply_filters('woonoow_process_subscription_payment', null, $gateway, $order, $subscription);
        if ($external_result !== null) {
            return $external_result ? true : false;
        }

        // 3. Fallback: Manual Payment
        // Set order to pending-payment
        $order->update_status('pending', __('Awaiting manual renewal payment', 'woonoow'));

        // Send renewal payment email to customer
        do_action('woonoow/subscription/renewal_payment_due', $subscription->id, $order);

        return 'manual'; // Return special status
    }

    /**
     * Handle successful renewal
     * 
     * @param int $subscription_id
     * @param \WC_Order $order
     */
    public static function handle_renewal_success($subscription_id, $order)
    {
        global $wpdb;

        $subscription = self::get($subscription_id);

        // Calculate next payment date
        // For early renewal, start from the current next_payment_date if it's in the future
        // Otherwise start from now (for expired/overdue subscriptions)
        $now = current_time('mysql');
        $base_date = $now;

        if ($subscription->next_payment_date && $subscription->next_payment_date > $now) {
            $base_date = $subscription->next_payment_date;
        }

        $next_payment = self::calculate_next_payment_date(
            $base_date,
            $subscription->billing_period,
            $subscription->billing_interval
        );

        // Check if subscription should end
        if ($subscription->end_date && strtotime($next_payment) > strtotime($subscription->end_date)) {
            $next_payment = null;
        }

        $wpdb->update(
            self::$table_subscriptions,
            [
                'status' => 'active',
                'next_payment_date' => $next_payment,
                'last_payment_date' => current_time('mysql'),
                'failed_payment_count' => 0,
            ],
            ['id' => $subscription_id],
            ['%s', '%s', '%s', '%d'],
            ['%d']
        );

        // Complete the order
        $order->payment_complete();

        do_action('woonoow/subscription/renewed', $subscription_id, $order);
    }

    /**
     * Handle failed renewal
     * 
     * @param int $subscription_id
     */
    private static function handle_renewal_failure($subscription_id)
    {
        global $wpdb;

        $subscription = self::get($subscription_id);
        $new_failed_count = $subscription->failed_payment_count + 1;

        // Get settings
        $settings = ModuleRegistry::get_settings('subscription');
        $max_attempts = $settings['expire_after_failed_attempts'] ?? 3;

        if ($new_failed_count >= $max_attempts) {
            // Mark as expired
            $wpdb->update(
                self::$table_subscriptions,
                [
                    'status' => 'expired',
                    'failed_payment_count' => $new_failed_count,
                ],
                ['id' => $subscription_id],
                ['%s', '%d'],
                ['%d']
            );

            do_action('woonoow/subscription/expired', $subscription_id, 'payment_failed');
        } else {
            // Just increment failed count
            $wpdb->update(
                self::$table_subscriptions,
                ['failed_payment_count' => $new_failed_count],
                ['id' => $subscription_id],
                ['%d'],
                ['%d']
            );

            do_action('woonoow/subscription/renewal_failed', $subscription_id, $new_failed_count);
        }
    }

    /**
     * Calculate next payment date
     * 
     * @param string $from_date
     * @param string $period
     * @param int $interval
     * @return string
     */
    public static function calculate_next_payment_date($from_date, $period, $interval = 1)
    {
        $interval = max(1, $interval);

        switch ($period) {
            case 'day':
                $modifier = "+ $interval days";
                break;
            case 'week':
                $modifier = "+ $interval weeks";
                break;
            case 'month':
                $modifier = "+ $interval months";
                break;
            case 'year':
                $modifier = "+ $interval years";
                break;
            default:
                $modifier = "+ 1 month";
        }

        return date('Y-m-d H:i:s', strtotime($from_date . ' ' . $modifier));
    }

    /**
     * Calculate subscription end date
     * 
     * @param string $start_date
     * @param string $period
     * @param int $interval
     * @param int $length
     * @return string
     */
    private static function calculate_end_date($start_date, $period, $interval, $length)
    {
        $total_periods = $interval * $length;

        switch ($period) {
            case 'day':
                $modifier = "+ $total_periods days";
                break;
            case 'week':
                $modifier = "+ $total_periods weeks";
                break;
            case 'month':
                $modifier = "+ $total_periods months";
                break;
            case 'year':
                $modifier = "+ $total_periods years";
                break;
            default:
                $modifier = "+ $total_periods months";
        }

        return date('Y-m-d H:i:s', strtotime($start_date . ' ' . $modifier));
    }

    /**
     * Get subscriptions due for renewal
     * 
     * @return array
     */
    public static function get_due_renewals()
    {
        global $wpdb;

        $now = current_time('mysql');

        return $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM " . self::$table_subscriptions . " 
             WHERE status = 'active' 
             AND next_payment_date IS NOT NULL 
             AND next_payment_date <= %s
             ORDER BY next_payment_date ASC",
            $now
        ));
    }

    /**
     * Get subscription orders
     * 
     * @param int $subscription_id
     * @return array
     */
    public static function get_orders($subscription_id)
    {
        global $wpdb;

        return $wpdb->get_results($wpdb->prepare(
            "SELECT so.*, p.post_status as order_status 
             FROM " . self::$table_subscription_orders . " so
             LEFT JOIN {$wpdb->posts} p ON so.order_id = p.ID
             WHERE so.subscription_id = %d
             ORDER BY so.created_at DESC",
            $subscription_id
        ));
    }
}
