<?php
/**
 * Webhook handlers for Subscription System
 *
 * @package    Subscription_System
 * @subpackage Subscription_System/includes
 */

// If this file is called directly, abort.
if (!defined('WPINC')) {
    die;
}

/**
 * Webhook handlers class
 */
class Subs_Sys_Webhooks {

    /**
     * Initialize the class
     */
    public function __construct() {
        // Register webhook endpoint
        add_action('rest_api_init', array($this, 'register_webhook_endpoint'));
    }

    /**
     * Register webhook endpoint
     */
    public function register_webhook_endpoint() {
        register_rest_route('subscription-system/v1', '/webhook', array(
            'methods' => 'POST',
            'callback' => array($this, 'handle_webhook'),
            'permission_callback' => '__return_true'
        ));
    }

    /**
     * Handle webhook request
     *
     * @param WP_REST_Request $request Request object
     * @return WP_REST_Response
     */
    public function handle_webhook($request) {
        try {
            // Initialize Stripe
            $stripe = new Subs_Sys_Stripe();
            
            // Get the request body
            $payload = $request->get_body();
            $sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];
            
            // Get webhook secret from settings
            $webhook_secret = get_option('subs_sys_stripe_webhook_secret');
            
            // Verify signature
            $event = \Stripe\Webhook::constructEvent(
                $payload, $sig_header, $webhook_secret
            );
            
            // Log the event
            Subscription_System::debug_log('Webhook received: ' . $event->type);
            
            // Process based on event type
            switch ($event->type) {
                case 'checkout.session.completed':
                    $this->handle_checkout_session_completed($event->data->object);
                    break;
                    
                case 'invoice.payment_succeeded':
                    $this->handle_invoice_payment_succeeded($event->data->object);
                    break;
                    
                case 'invoice.payment_failed':
                    $this->handle_invoice_payment_failed($event->data->object);
                    break;
                    
                case 'customer.subscription.updated':
                    $this->handle_subscription_updated($event->data->object);
                    break;
                    
                case 'customer.subscription.deleted':
                    $this->handle_subscription_deleted($event->data->object);
                    break;
                    
                default:
                    // Log unhandled event
                    Subscription_System::debug_log('Unhandled webhook event: ' . $event->type);
            }
            
            // Return success
            return new WP_REST_Response(array('status' => 'success'), 200);
            
        } catch (\UnexpectedValueException $e) {
            // Invalid payload
            Subscription_System::debug_log('Webhook error: ' . $e->getMessage());
            return new WP_REST_Response(array('status' => 'error', 'message' => 'Invalid payload'), 400);
            
        } catch (\Stripe\Exception\SignatureVerificationException $e) {
            // Invalid signature
            Subscription_System::debug_log('Webhook signature verification failed: ' . $e->getMessage());
            return new WP_REST_Response(array('status' => 'error', 'message' => 'Invalid signature'), 400);
            
        } catch (Exception $e) {
            // Other error
            Subscription_System::debug_log('Webhook error: ' . $e->getMessage());
            return new WP_REST_Response(array('status' => 'error', 'message' => $e->getMessage()), 500);
        }
    }
    
    /**
     * Handle checkout.session.completed event
     *
     * @param object $session Checkout session
     */
    private function handle_checkout_session_completed($session) {
        // Get session details
        $session_id = $session->id;
        Subscription_System::debug_log('Processing completed checkout session: ' . $session_id);
        
        // Check if subscription already exists for this session
        global $wpdb;
        $subscription_table = $wpdb->prefix . 'subscriptions';
        $existing_subscription = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $subscription_table WHERE payment_id = %s",
            $session_id
        ));
        
        if ($existing_subscription) {
            Subscription_System::debug_log('Subscription already exists for session: ' . $session_id);
            return;
        }
        
        // Get customer details
        $customer_email = $session->customer_details->email;
        $customer_name = $session->customer_details->name;
        
        // Get plan ID from client reference ID or metadata
        $plan_id = intval($session->client_reference_id);
        if (!$plan_id) {
            // Try to get from metadata
            $plan_id = isset($session->metadata->plan_id) ? intval($session->metadata->plan_id) : 0;
        }
        
        // Get user ID
        $user_id = isset($session->metadata->user_id) ? intval($session->metadata->user_id) : 0;
        if ($user_id === 0 && !empty($customer_email)) {
            $user = get_user_by('email', $customer_email);
            if ($user) {
                $user_id = $user->ID;
            }
        }
        
        // Get plan details
        $table_name = $wpdb->prefix . 'subscription_plans';
        $plan = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table_name WHERE id = %d", $plan_id));
        
        if (!$plan) {
            Subscription_System::debug_log('Invalid plan ID: ' . $plan_id);
            return;
        }
        
        // Get subscription details from session
        $stripe_subscription_id = $session->subscription;
        $subscription_data = null;
        
        if ($stripe_subscription_id) {
            $stripe = new Subs_Sys_Stripe();
            $subscription_data = \Stripe\Subscription::retrieve($stripe_subscription_id);
        }
        
        // Create subscription record in database
        $subscription_data = array(
            'user_id' => intval($user_id),
            'plan_id' => intval($plan_id),
            'customer_email' => sanitize_email($customer_email),
            'customer_name' => sanitize_text_field($customer_name),
            'payment_id' => sanitize_text_field($subscription_data ? $subscription_data->id : $session_id),
            'amount' => floatval($plan->price),
            'status' => 'active',
            'start_date' => $subscription_data ? date('Y-m-d H:i:s', $subscription_data->current_period_start) : current_time('mysql'),
            'end_date' => $subscription_data ? date('Y-m-d H:i:s', $subscription_data->current_period_end) : date('Y-m-d H:i:s', strtotime('+1 month', current_time('timestamp'))),
            'created_at' => current_time('mysql'),
            'updated_at' => current_time('mysql')
        );
        
        $result = $wpdb->insert(
            $subscription_table,
            $subscription_data
        );
        
        if ($result === false) {
            Subscription_System::debug_log('Failed to insert subscription: ' . $wpdb->last_error);
            return;
        }
        
        $subscription_id = $wpdb->insert_id;
        Subscription_System::debug_log('Subscription created with ID: ' . $subscription_id);
        
        // Get the newly created subscription
        $subscription = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $subscription_table WHERE id = %d",
            $subscription_id
        ));
        
        // Create order for the new subscription
        $this->create_order_for_subscription($subscription, $session, $plan);
        
        // Send confirmation email
        $this->send_confirmation_email($customer_email, $customer_name, $plan);
    }
    
    /**
     * Handle invoice.payment_succeeded event
     *
     * @param object $invoice Invoice object
     */
    private function handle_invoice_payment_succeeded($invoice) {
        // Get subscription ID
        $subscription_id = $invoice->subscription;
        if (!$subscription_id) {
            Subscription_System::debug_log('No subscription ID in invoice: ' . $invoice->id);
            return;
        }
        
        // Get subscription from Stripe
        $stripe = new Subs_Sys_Stripe();
        $stripe_subscription = \Stripe\Subscription::retrieve($subscription_id);
        
        // Get local subscription
        global $wpdb;
        $subscription_table = $wpdb->prefix . 'subscriptions';
        $subscription = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $subscription_table WHERE payment_id = %s",
            $subscription_id
        ));
        
        if (!$subscription) {
            Subscription_System::debug_log('Local subscription not found for Stripe subscription: ' . $subscription_id);
            return;
        }
        
        // Update subscription dates
        $wpdb->update(
            $subscription_table,
            array(
                'status' => 'active',
                'start_date' => date('Y-m-d H:i:s', $stripe_subscription->current_period_start),
                'end_date' => date('Y-m-d H:i:s', $stripe_subscription->current_period_end),
                'updated_at' => current_time('mysql')
            ),
            array('id' => $subscription->id)
        );
        
        // Create order for renewal
        $this->create_renewal_order($subscription, $invoice);
    }
    
    /**
     * Handle invoice.payment_failed event
     *
     * @param object $invoice Invoice object
     */
    private function handle_invoice_payment_failed($invoice) {
        // Get subscription ID
        $subscription_id = $invoice->subscription;
        if (!$subscription_id) {
            Subscription_System::debug_log('No subscription ID in invoice: ' . $invoice->id);
            return;
        }
        
        // Get local subscription
        global $wpdb;
        $subscription_table = $wpdb->prefix . 'subscriptions';
        $subscription = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $subscription_table WHERE payment_id = %s",
            $subscription_id
        ));
        
        if (!$subscription) {
            Subscription_System::debug_log('Local subscription not found for Stripe subscription: ' . $subscription_id);
            return;
        }
        
        // Update subscription status
        $wpdb->update(
            $subscription_table,
            array(
                'status' => 'past_due',
                'updated_at' => current_time('mysql')
            ),
            array('id' => $subscription->id)
        );
        
        // Notify customer
        $this->send_payment_failed_email($subscription);
    }
    
    /**
     * Handle customer.subscription.updated event
     *
     * @param object $stripe_subscription Stripe subscription object
     */
    private function handle_subscription_updated($stripe_subscription) {
        // Get local subscription
        global $wpdb;
        $subscription_table = $wpdb->prefix . 'subscriptions';
        $subscription = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $subscription_table WHERE payment_id = %s",
            $stripe_subscription->id
        ));
        
        if (!$subscription) {
            Subscription_System::debug_log('Local subscription not found for Stripe subscription: ' . $stripe_subscription->id);
            return;
        }
        
        // Map Stripe status to local status
        $status_map = array(
            'active' => 'active',
            'past_due' => 'past_due',
            'unpaid' => 'past_due',
            'canceled' => 'canceled',
            'incomplete' => 'pending',
            'incomplete_expired' => 'canceled',
            'trialing' => 'active',
            'paused' => 'paused'
        );
        
        $status = isset($status_map[$stripe_subscription->status]) ? 
            $status_map[$stripe_subscription->status] : 'active';
        
        // Update subscription
        $wpdb->update(
            $subscription_table,
            array(
                'status' => $status,
                'start_date' => date('Y-m-d H:i:s', $stripe_subscription->current_period_start),
                'end_date' => date('Y-m-d H:i:s', $stripe_subscription->current_period_end),
                'updated_at' => current_time('mysql')
            ),
            array('id' => $subscription->id)
        );
    }
    
    /**
     * Handle customer.subscription.deleted event
     *
     * @param object $stripe_subscription Stripe subscription object
     */
    private function handle_subscription_deleted($stripe_subscription) {
        // Get local subscription
        global $wpdb;
        $subscription_table = $wpdb->prefix . 'subscriptions';
        $subscription = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $subscription_table WHERE payment_id = %s",
            $stripe_subscription->id
        ));
        
        if (!$subscription) {
            Subscription_System::debug_log('Local subscription not found for Stripe subscription: ' . $stripe_subscription->id);
            return;
        }
        
        // Update subscription status
        $wpdb->update(
            $subscription_table,
            array(
                'status' => 'canceled',
                'updated_at' => current_time('mysql')
            ),
            array('id' => $subscription->id)
        );
    }
    
    /**
     * Create an order for a subscription
     *
     * @param object $subscription Subscription object
     * @param object $session Stripe session object
     * @param object $plan Plan object
     * @return int|false Order ID or false on failure
     */
    private function create_order_for_subscription($subscription, $session, $plan) {
        // Get payment details
        $payment_method = 'stripe';
        $transaction_date = current_time('mysql');
        
        // Get payment intent for more details
        $payment_intent = $session->payment_intent;
        if ($payment_intent) {
            if (is_string($payment_intent)) {
                $stripe = new Subs_Sys_Stripe();
                $payment_intent = \Stripe\PaymentIntent::retrieve($payment_intent);
            }
            
            // Get payment method details
            if (!empty($payment_intent->charges->data)) {
                $charge = $payment_intent->charges->data[0];
                $payment_method = $charge->payment_method_details->type;
                $transaction_date = date('Y-m-d H:i:s', $charge->created);
            }
        }
        
        // Create order data
        $order_data = [
            'user_id' => $subscription->user_id,
            'subscription_id' => $subscription->id,
            'order_number' => 'ORD-' . time() . '-' . mt_rand(1000, 9999),
            'payment_id' => $session->id,
            'amount' => $subscription->amount,
            'status' => 'completed',
            'payment_method' => $payment_method,
            'billing_name' => $subscription->customer_name,
            'billing_email' => $subscription->customer_email,
            'transaction_date' => $transaction_date,
        ];
        
        // Create the order
        return Subs_Sys_Order::create_order($order_data);
    }
    
    /**
     * Create a renewal order
     *
     * @param object $subscription Subscription object
     * @param object $invoice Stripe invoice object
     * @return int|false Order ID or false on failure
     */
    private function create_renewal_order($subscription, $invoice) {
        // Create order data
        $order_data = [
            'user_id' => $subscription->user_id,
            'subscription_id' => $subscription->id,
            'order_number' => 'ORD-' . time() . '-' . mt_rand(1000, 9999),
            'payment_id' => $invoice->id,
            'amount' => $invoice->amount_paid / 100, // Convert from cents
            'status' => 'completed',
            'payment_method' => 'stripe',
            'billing_name' => $subscription->customer_name,
            'billing_email' => $subscription->customer_email,
            'transaction_date' => date('Y-m-d H:i:s', $invoice->created),
        ];
        
        // Create the order
        return Subs_Sys_Order::create_order($order_data);
    }
    
    /**
     * Send confirmation email
     *
     * @param string $email Customer email
     * @param string $name Customer name
     * @param object $plan Subscription plan
     */
    private function send_confirmation_email($email, $name, $plan) {
        // translators: %s is the website name
        /* translators: %s: website name */
            $subject = sprintf(__('Your subscription to %s has been confirmed', 'subscription-system'), get_bloginfo('name'));
        
        // translators: %s is the customer name
        $message = sprintf(__('Hello %s,', 'subscription-system'), $name) . "\n\n";
        // translators: %s is the plan name
        $message .= sprintf(__('Thank you for subscribing to the %s plan.', 'subscription-system'), $plan->plan_name) . "\n\n";
        $message .= sprintf(__('Subscription details:', 'subscription-system')) . "\n";
        // translators: %s is the plan name
        $message .= sprintf(__('Plan: %s', 'subscription-system'), $plan->plan_name) . "\n";
        // translators: %s is the formatted price amount
        $message .= sprintf(__('Price: $%s per month', 'subscription-system'), number_format($plan->price, 2)) . "\n";
        // translators: %s is the formatted start date
        $message .= sprintf(__('Start date: %s', 'subscription-system'), date_i18n(get_option('date_format'))) . "\n\n";
        $message .= sprintf(__('If you have any questions, please contact us.', 'subscription-system')) . "\n\n";
        $message .= sprintf(__('Thank you,', 'subscription-system')) . "\n";
        $message .= get_bloginfo('name');
        
        $headers = array('Content-Type: text/plain; charset=UTF-8');
        
        wp_mail($email, $subject, $message, $headers);
    }
    
    /**
     * Send payment failed email
     *
     * @param object $subscription Subscription object
     */
    private function send_payment_failed_email($subscription) {
        // translators: %s is the website name
        $subject = sprintf(__('Payment failed for your %s subscription', 'subscription-system'), get_bloginfo('name'));
        
        // translators: %s is the customer name
        $message = sprintf(__('Hello %s,', 'subscription-system'), $subscription->customer_name) . "\n\n";
        $message .= sprintf(__('We were unable to process your payment for your subscription.', 'subscription-system')) . "\n\n";
        $message .= sprintf(__('Please update your payment method to continue your subscription.', 'subscription-system')) . "\n\n";
        // translators: %s is the account page URL
        $message .= sprintf(__('You can update your payment details here: %s', 'subscription-system'), get_permalink(get_option('account_page'))) . "\n\n";
        $message .= sprintf(__('If you have any questions, please contact us.', 'subscription-system')) . "\n\n";
        $message .= sprintf(__('Thank you,', 'subscription-system')) . "\n";
        $message .= get_bloginfo('name');
        
        $headers = array('Content-Type: text/plain; charset=UTF-8');
        
        wp_mail($subscription->customer_email, $subject, $message, $headers);
    }
}

// Initialize the class
new Subs_Sys_Webhooks(); 