<?php
/**
 * Email handling for Maple Carts.
 *
 * @package MapleCarts
 */

defined( 'ABSPATH' ) || exit;

/**
 * Emails class.
 */
class Maple_Carts_Emails {

    /**
     * Single instance.
     *
     * @var Maple_Carts_Emails
     */
    private static $instance = null;

    /**
     * Get instance.
     *
     * @return Maple_Carts_Emails
     */
    public static function instance() {
        if ( is_null( self::$instance ) ) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Constructor.
     */
    private function __construct() {
        // Cron hook for sending emails.
        add_action( 'maple_carts_send_emails', [ $this, 'process_pending_emails' ] );
        
        // AJAX for preview email.
        add_action( 'wp_ajax_maple_carts_preview_email', [ $this, 'ajax_preview_email' ] );
        add_action( 'wp_ajax_maple_carts_send_test_email', [ $this, 'ajax_send_test_email' ] );
    }

    /**
     * Process pending emails.
     * Includes lock to prevent overlapping cron runs.
     */
    public function process_pending_emails() {
        // Skip if Mailjet mode is enabled - emails are handled by Mailjet automations.
        if ( 'mailjet' === Maple_Carts::get_option( 'email_mode', 'builtin' ) ) {
            return;
        }

        // Prevent overlapping cron runs.
        if ( get_transient( 'maple_carts_sending_emails' ) ) {
            return;
        }
        set_transient( 'maple_carts_sending_emails', 1, 300 ); // 5 minute lock.

        $pending = Maple_Carts_DB::get_pending_emails();

        foreach ( $pending as $email_data ) {
            $this->send_email( $email_data );
        }

        delete_transient( 'maple_carts_sending_emails' );
    }

    /**
     * Send a recovery email.
     *
     * @param object $email_data Email data from database.
     * @return bool
     */
    public function send_email( $email_data ) {
        // Validate email address.
        if ( ! is_email( $email_data->email ) ) {
            Maple_Carts_DB::update_email_log( $email_data->id, 'failed', [ 'error_message' => 'Invalid email address' ] );
            return false;
        }

        // Check if cart still qualifies.
        $cart = Maple_Carts_DB::get_cart_by_session( $email_data->session_id );
        if ( ! $cart || 'abandoned' !== $cart->status || $cart->unsubscribed ) {
            Maple_Carts_DB::update_email_log( $email_data->id, 'cancelled' );
            return false;
        }

        // Check for out-of-stock products.
        $cart_contents = maybe_unserialize( $email_data->cart_contents );
        if ( $this->cart_has_out_of_stock( $cart_contents ) ) {
            Maple_Carts_DB::update_email_log( $email_data->id, 'cancelled', [ 'error_message' => 'Products out of stock' ] );
            return false;
        }

        // Generate coupon if needed.
        $coupon_code = '';
        if ( $email_data->include_coupon ) {
            $coupon_code = $this->generate_coupon(
                $email_data->coupon_type,
                $email_data->coupon_amount,
                $email_data->coupon_expires_days,
                $email_data->cart_id
            );
            
            // Store coupon code in log.
            global $wpdb;
            $wpdb->update(
                $wpdb->prefix . MAPLE_CARTS_EMAIL_LOG_TABLE,
                [ 'coupon_code' => $coupon_code ],
                [ 'id' => $email_data->id ]
            );
        }

        // Generate recovery URL with coupon.
        $recovery_url = Maple_Carts_Tracking::instance()->generate_recovery_url(
            $email_data->session_id,
            $coupon_code
        );

        // Add UTM parameters for analytics attribution.
        $recovery_url = add_query_arg( [
            'utm_source'   => 'maple_carts',
            'utm_medium'   => 'email',
            'utm_campaign' => 'cart_recovery',
            'utm_content'  => 'email_' . $email_data->email_template_id,
        ], $recovery_url );

        // Parse email content.
        $subject = $this->parse_template( $email_data->subject, $email_data, $coupon_code, $recovery_url );
        $body    = $this->parse_template( $email_data->body, $email_data, $coupon_code, $recovery_url );

        // Send email.
        $sent = $this->dispatch_email( $email_data->email, $subject, $body );

        if ( $sent ) {
            Maple_Carts_DB::update_email_log( $email_data->id, 'sent' );
            
            /**
             * Fires after a recovery email is sent.
             *
             * @param object $email_data Email data.
             * @param string $subject    Email subject.
             */
            do_action( 'maple_carts_email_sent', $email_data, $subject );
            
            return true;
        } else {
            Maple_Carts_DB::update_email_log( $email_data->id, 'failed', [ 'error_message' => 'Failed to send email' ] );
            return false;
        }
    }

    /**
     * Parse email template with placeholders.
     *
     * @param string $template     Template string.
     * @param object $email_data   Email data.
     * @param string $coupon_code  Coupon code.
     * @param string $recovery_url Recovery URL.
     * @return string
     */
    private function parse_template( $template, $email_data, $coupon_code, $recovery_url ) {
        $cart_contents = maybe_unserialize( $email_data->cart_contents );
        
        // Customer name fallback.
        $customer_name = $email_data->customer_name;
        if ( empty( $customer_name ) ) {
            $customer_name = __( 'there', 'maple-carts' );
        }

        // Generate cart contents HTML.
        $cart_html = $this->generate_cart_html( $cart_contents, $email_data->currency );

        // Generate product names list.
        $product_names = [];
        if ( is_array( $cart_contents ) ) {
            foreach ( $cart_contents as $item ) {
                $product_names[] = $item['name'];
            }
        }

        // Coupon display amount.
        $coupon_display = '';
        if ( ! empty( $coupon_code ) ) {
            if ( 'percent' === $email_data->coupon_type ) {
                $coupon_display = $email_data->coupon_amount . '%';
            } else {
                $coupon_display = wc_price( $email_data->coupon_amount );
            }
        }

        // Unsubscribe link (using signed URL from Tracking class).
        $unsubscribe_url  = Maple_Carts_Tracking::instance()->generate_unsubscribe_url( $email_data->session_id );
        $unsubscribe_link = sprintf(
            '<a href="%s" style="color:#999;">%s</a>',
            esc_url( $unsubscribe_url ),
            __( 'Unsubscribe from these emails', 'maple-carts' )
        );

        // Delete data link for GDPR compliance (optional).
        $delete_data_link = '';
        if ( 'yes' === Maple_Carts::get_option( 'show_delete_link', 'no' ) ) {
            $delete_data_url  = Maple_Carts_Privacy::generate_deletion_url( $email_data->session_id );
            $delete_data_link = sprintf(
                ' | <a href="%s" style="color:#999;">%s</a>',
                esc_url( $delete_data_url ),
                __( 'Delete my data', 'maple-carts' )
            );
        }

        // Replacements.
        $replacements = [
            '{{customer_name}}'    => esc_html( $customer_name ),
            '{{customer_email}}'   => esc_html( $email_data->email ),
            '{{cart_contents}}'    => $cart_html,
            '{{cart_total}}'       => wc_price( $email_data->cart_total, [ 'currency' => $email_data->currency ] ),
            '{{product_names}}'    => esc_html( implode( ', ', $product_names ) ),
            '{{recovery_url}}'     => esc_url( $recovery_url ),
            '{{coupon_code}}'      => esc_html( $coupon_code ),
            '{{coupon_amount}}'    => $coupon_display,
            '{{coupon_expires}}'   => $email_data->coupon_expires_days,
            '{{site_name}}'        => esc_html( get_bloginfo( 'name' ) ),
            '{{site_url}}'         => esc_url( home_url() ),
            '{{unsubscribe_link}}' => $unsubscribe_link,
            '{{delete_data_link}}' => $delete_data_link,
            '{{current_date}}'     => date_i18n( get_option( 'date_format' ) ),
        ];

        /**
         * Filter email template replacements.
         *
         * @param array  $replacements Placeholder replacements.
         * @param object $email_data   Email data.
         */
        $replacements = apply_filters( 'maple_carts_email_replacements', $replacements, $email_data );

        return str_replace( array_keys( $replacements ), array_values( $replacements ), $template );
    }

    /**
     * Generate cart contents HTML.
     *
     * @param array  $cart_contents Cart contents.
     * @param string $currency      Currency code.
     * @return string
     */
    private function generate_cart_html( $cart_contents, $currency = 'USD' ) {
        if ( empty( $cart_contents ) || ! is_array( $cart_contents ) ) {
            return '';
        }

        $html = '<table style="width:100%;border-collapse:collapse;margin:20px 0;">';
        $html .= '<thead><tr style="background:#f7f7f7;">';
        $html .= '<th style="padding:12px;text-align:left;border-bottom:2px solid #ddd;">' . __( 'Product', 'maple-carts' ) . '</th>';
        $html .= '<th style="padding:12px;text-align:center;border-bottom:2px solid #ddd;">' . __( 'Qty', 'maple-carts' ) . '</th>';
        $html .= '<th style="padding:12px;text-align:right;border-bottom:2px solid #ddd;">' . __( 'Price', 'maple-carts' ) . '</th>';
        $html .= '</tr></thead><tbody>';

        $total = 0;
        foreach ( $cart_contents as $item ) {
            $line_total = $item['price'] * $item['quantity'];
            $total += $line_total;

            $html .= '<tr>';
            $html .= '<td style="padding:12px;border-bottom:1px solid #eee;">';
            
            if ( ! empty( $item['image'] ) ) {
                $html .= '<img src="' . esc_url( $item['image'] ) . '" alt="" style="width:50px;height:50px;object-fit:cover;margin-right:10px;vertical-align:middle;">';
            }
            
            $html .= '<span style="vertical-align:middle;">' . esc_html( $item['name'] ) . '</span>';
            $html .= '</td>';
            $html .= '<td style="padding:12px;text-align:center;border-bottom:1px solid #eee;">' . esc_html( $item['quantity'] ) . '</td>';
            $html .= '<td style="padding:12px;text-align:right;border-bottom:1px solid #eee;">' . wc_price( $line_total, [ 'currency' => $currency ] ) . '</td>';
            $html .= '</tr>';
        }

        $html .= '</tbody></table>';

        return $html;
    }

    /**
     * Check if cart has out of stock products.
     *
     * @param array $cart_contents Cart contents.
     * @return bool
     */
    private function cart_has_out_of_stock( $cart_contents ) {
        if ( empty( $cart_contents ) || ! is_array( $cart_contents ) ) {
            return false;
        }

        foreach ( $cart_contents as $item ) {
            $product_id = ! empty( $item['variation_id'] ) ? $item['variation_id'] : $item['product_id'];
            $product    = wc_get_product( $product_id );

            if ( ! $product || ! $product->is_in_stock() ) {
                return true;
            }
        }

        return false;
    }

    /**
     * Generate a coupon code.
     *
     * @param string $type         Coupon type (percent, fixed_cart).
     * @param float  $amount       Discount amount.
     * @param int    $expires_days Days until expiry.
     * @param int    $cart_id      Cart ID for reference.
     * @return string
     */
    public function generate_coupon( $type, $amount, $expires_days, $cart_id ) {
        // Check if cart already has a coupon (prevents double creation on cron misfire).
        $existing_coupon = $this->get_cart_coupon( $cart_id );
        if ( $existing_coupon ) {
            return $existing_coupon;
        }

        $coupon_code = 'MAPLE-' . strtoupper( wp_generate_password( 8, false, false ) );

        $coupon = new WC_Coupon();
        $coupon->set_code( $coupon_code );
        $coupon->set_discount_type( $type );
        $coupon->set_amount( $amount );
        $coupon->set_individual_use( true );
        $coupon->set_usage_limit( 1 );
        $coupon->set_usage_limit_per_user( 1 );

        if ( $expires_days > 0 ) {
            $expiry = strtotime( "+{$expires_days} days" );
            $coupon->set_date_expires( $expiry );
        }

        $coupon->save();

        // Mark as Maple Carts coupon for cleanup with creation timestamp.
        update_post_meta( $coupon->get_id(), '_maple_carts_coupon', '1' );
        update_post_meta( $coupon->get_id(), '_maple_carts_cart_id', $cart_id );
        update_post_meta( $coupon->get_id(), '_maple_carts_created_at', time() );

        return $coupon_code;
    }

    /**
     * Get existing coupon for a cart (prevents double creation).
     *
     * @param int $cart_id Cart ID.
     * @return string|null Coupon code or null.
     */
    private function get_cart_coupon( $cart_id ) {
        global $wpdb;
        
        // Check email log for existing coupon.
        $log_table = $wpdb->prefix . MAPLE_CARTS_EMAIL_LOG_TABLE;
        $coupon    = $wpdb->get_var( $wpdb->prepare(
            "SELECT coupon_code FROM {$log_table} 
             WHERE cart_id = %d AND coupon_code IS NOT NULL AND coupon_code != '' 
             LIMIT 1",
            $cart_id
        ) );

        if ( $coupon ) {
            // Verify coupon still exists in WooCommerce.
            $coupon_id = wc_get_coupon_id_by_code( $coupon );
            if ( $coupon_id ) {
                return $coupon;
            }
        }

        return null;
    }

    /**
     * Dispatch email using wp_mail (works with any SMTP plugin).
     *
     * @param string $to      Recipient email.
     * @param string $subject Email subject.
     * @param string $body    Email body (HTML).
     * @return bool
     */
    private function dispatch_email( $to, $subject, $body ) {
        // Sanitize header values to prevent header injection.
        $from_name  = $this->sanitize_header_value( Maple_Carts::get_option( 'from_name', get_bloginfo( 'name' ) ) );
        $from_email = $this->sanitize_header_value( Maple_Carts::get_option( 'from_email', get_option( 'admin_email' ) ) );
        $reply_to   = $this->sanitize_header_value( Maple_Carts::get_option( 'reply_to', get_option( 'admin_email' ) ) );

        // Validate email addresses.
        if ( ! is_email( $from_email ) ) {
            $from_email = get_option( 'admin_email' );
        }
        if ( ! is_email( $reply_to ) ) {
            $reply_to = get_option( 'admin_email' );
        }

        // Wrap in basic HTML template.
        $html_body = $this->wrap_email_html( $body );

        // Generate plain text alternative for better spam scores.
        $plain_body = $this->generate_plain_text( $body );

        // Create multipart boundary.
        $boundary = 'maple_carts_' . md5( uniqid( time() ) );

        $headers = [
            "From: {$from_name} <{$from_email}>",
            "Reply-To: {$reply_to}",
            "Content-Type: multipart/alternative; boundary=\"{$boundary}\"",
        ];

        // Build multipart message.
        $message = "--{$boundary}\r\n";
        $message .= "Content-Type: text/plain; charset=UTF-8\r\n";
        $message .= "Content-Transfer-Encoding: 8bit\r\n\r\n";
        $message .= $plain_body . "\r\n\r\n";
        $message .= "--{$boundary}\r\n";
        $message .= "Content-Type: text/html; charset=UTF-8\r\n";
        $message .= "Content-Transfer-Encoding: 8bit\r\n\r\n";
        $message .= $html_body . "\r\n\r\n";
        $message .= "--{$boundary}--";

        /**
         * Filter the email before sending.
         *
         * @param array $email Email data.
         */
        $email = apply_filters( 'maple_carts_before_send_email', [
            'to'      => $to,
            'subject' => $subject,
            'body'    => $message,
            'headers' => $headers,
        ] );

        // Use wp_mail which integrates with SMTP plugins.
        $sent = wp_mail( $email['to'], $email['subject'], $email['body'], $email['headers'] );

        // Retry once on failure.
        if ( ! $sent ) {
            sleep( 1 );
            $sent = wp_mail( $email['to'], $email['subject'], $email['body'], $email['headers'] );
        }

        return $sent;
    }

    /**
     * Generate plain text version of HTML email.
     *
     * @param string $html HTML content.
     * @return string Plain text content.
     */
    private function generate_plain_text( $html ) {
        // Convert common HTML elements.
        $text = $html;
        
        // Convert links to text format.
        $text = preg_replace( '/<a[^>]+href=["\']([^"\']+)["\'][^>]*>([^<]+)<\/a>/i', '$2 ($1)', $text );
        
        // Convert line breaks and paragraphs.
        $text = preg_replace( '/<br\s*\/?>/i', "\n", $text );
        $text = preg_replace( '/<\/p>/i', "\n\n", $text );
        $text = preg_replace( '/<\/div>/i', "\n", $text );
        $text = preg_replace( '/<\/h[1-6]>/i', "\n\n", $text );
        
        // Convert list items.
        $text = preg_replace( '/<li[^>]*>/i', "• ", $text );
        $text = preg_replace( '/<\/li>/i', "\n", $text );
        
        // Strip remaining tags.
        $text = wp_strip_all_tags( $text );
        
        // Decode HTML entities.
        $text = html_entity_decode( $text, ENT_QUOTES, 'UTF-8' );
        
        // Clean up whitespace.
        $text = preg_replace( '/[ \t]+/', ' ', $text );
        $text = preg_replace( '/\n{3,}/', "\n\n", $text );
        $text = trim( $text );
        
        return $text;
    }

    /**
     * Sanitize a value for use in email headers.
     * Prevents header injection attacks by removing newlines and control characters.
     *
     * @param string $value The value to sanitize.
     * @return string
     */
    private function sanitize_header_value( $value ) {
        // Remove any newlines, carriage returns, and null bytes.
        $value = preg_replace( '/[\r\n\0]/', '', $value );
        // Remove any remaining control characters.
        $value = preg_replace( '/[\x00-\x1F\x7F]/', '', $value );
        return trim( $value );
    }

    /**
     * Wrap email content in HTML template.
     *
     * @param string $content Email content.
     * @return string
     */
    private function wrap_email_html( $content ) {
        $template = '<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body style="margin:0;padding:0;background-color:#f4f4f4;font-family:-apple-system,BlinkMacSystemFont,\'Segoe UI\',Roboto,Oxygen-Sans,Ubuntu,Cantarell,\'Helvetica Neue\',sans-serif;">
    <table role="presentation" style="width:100%;border-collapse:collapse;">
        <tr>
            <td style="padding:40px 20px;">
                <table role="presentation" style="max-width:600px;margin:0 auto;background:#ffffff;border-radius:8px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.05);">
                    <tr>
                        <td style="padding:40px;">
                            {{content}}
                        </td>
                    </tr>
                </table>
            </td>
        </tr>
    </table>
</body>
</html>';

        return str_replace( '{{content}}', wpautop( $content ), $template );
    }

    /**
     * AJAX handler for email preview.
     */
    public function ajax_preview_email() {
        check_ajax_referer( 'maple_carts_admin', 'nonce' );

        if ( ! current_user_can( 'manage_woocommerce' ) ) {
            wp_send_json_error( 'Permission denied' );
        }

        $template_id = isset( $_POST['template_id'] ) ? absint( $_POST['template_id'] ) : 0;
        $subject     = isset( $_POST['subject'] ) ? sanitize_text_field( wp_unslash( $_POST['subject'] ) ) : '';
        $body        = isset( $_POST['body'] ) ? wp_kses_post( wp_unslash( $_POST['body'] ) ) : '';

        // Create dummy data for preview.
        $dummy_data = (object) [
            'email'               => 'customer@example.com',
            'customer_name'       => 'John Doe',
            'cart_contents'       => maybe_serialize( [
                [
                    'product_id' => 1,
                    'name'       => 'Sample Product',
                    'price'      => 29.99,
                    'quantity'   => 2,
                    'image'      => '',
                ],
            ] ),
            'cart_total'          => 59.98,
            'currency'            => get_woocommerce_currency(),
            'session_id'          => 'preview-session',
            'include_coupon'      => true,
            'coupon_type'         => 'percent',
            'coupon_amount'       => 10,
            'coupon_expires_days' => 7,
        ];

        $parsed_subject = $this->parse_template( $subject, $dummy_data, 'PREVIEW10', '#' );
        $parsed_body    = $this->parse_template( $body, $dummy_data, 'PREVIEW10', '#' );
        $html_body      = $this->wrap_email_html( $parsed_body );

        wp_send_json_success( [
            'subject' => $parsed_subject,
            'body'    => $html_body,
        ] );
    }

    /**
     * AJAX handler for sending test email.
     */
    public function ajax_send_test_email() {
        check_ajax_referer( 'maple_carts_admin', 'nonce' );

        if ( ! current_user_can( 'manage_woocommerce' ) ) {
            wp_send_json_error( 'Permission denied' );
        }

        $email   = isset( $_POST['email'] ) ? sanitize_email( wp_unslash( $_POST['email'] ) ) : '';
        $subject = isset( $_POST['subject'] ) ? sanitize_text_field( wp_unslash( $_POST['subject'] ) ) : '';
        $body    = isset( $_POST['body'] ) ? wp_kses_post( wp_unslash( $_POST['body'] ) ) : '';

        if ( ! is_email( $email ) ) {
            wp_send_json_error( __( 'Invalid email address', 'maple-carts' ) );
        }

        // Create dummy data.
        $dummy_data = (object) [
            'email'               => $email,
            'customer_name'       => 'Test Customer',
            'cart_contents'       => maybe_serialize( [
                [
                    'product_id' => 1,
                    'name'       => 'Test Product',
                    'price'      => 49.99,
                    'quantity'   => 1,
                    'image'      => '',
                ],
            ] ),
            'cart_total'          => 49.99,
            'currency'            => get_woocommerce_currency(),
            'session_id'          => 'test-session',
            'include_coupon'      => true,
            'coupon_type'         => 'percent',
            'coupon_amount'       => 10,
            'coupon_expires_days' => 7,
        ];

        $parsed_subject = $this->parse_template( $subject, $dummy_data, 'TEST10OFF', '#' );
        $parsed_body    = $this->parse_template( $body, $dummy_data, 'TEST10OFF', '#' );

        $sent = $this->dispatch_email( $email, '[TEST] ' . $parsed_subject, $parsed_body );

        if ( $sent ) {
            wp_send_json_success( __( 'Test email sent successfully!', 'maple-carts' ) );
        } else {
            wp_send_json_error( __( 'Failed to send test email. Check your email configuration.', 'maple-carts' ) );
        }
    }
}
