<?php
if ( ! defined( 'ABSPATH' ) ) {
    exit; // Exit if accessed directly.
}

class CGC_WooCommerce {

    public function __construct() {
        add_action( 'woocommerce_before_calculate_totals', array( $this, 'set_custom_cart_item_price' ), 10, 1 );

        // Copies the cart-item bookkeeping data (card_id, addon selections,
        // etc.) added in CGC_Rest_API::add_to_cart() onto the real order
        // line item - without this, WooCommerce never persists arbitrary
        // cart_item_data keys past checkout, and every get_meta('card_id')
        // call below (gift_card_approve/handle_payment_complete/
        // handle_view_order) would silently no-op on a real order.
        add_action( 'woocommerce_checkout_create_order_line_item', array( $this, 'copy_cart_item_data_to_order_item' ), 10, 4 );
        add_filter( 'woocommerce_hidden_order_itemmeta', array( $this, 'hide_internal_order_itemmeta' ) );

        add_action( 'woocommerce_thankyou', array( $this, 'gift_card_approve' ), 10, 1);

        // A real coupon must only ever be generated once payment is actually
        // confirmed (woocommerce_thankyou fires for pending/on-hold orders
        // too - eg. COD/BACS - which is NOT the same as "paid", and would
        // otherwise let someone mint a free usable discount code just by
        // placing an order and never paying). woocommerce_payment_complete
        // fires once, reliably, only on confirmed payment, including via
        // async/off-site gateway webhooks - so coupon generation is
        // triggered from there, not solely from the thank-you page hook.
        add_action( 'woocommerce_payment_complete', array( $this, 'handle_payment_complete' ), 10, 1 );

        // Fallback for customers who never land back on the immediate
        // thank-you page (eg. closed the tab during an off-site gateway
        // redirect) - re-render still needs a real browser/canvas, so this
        // gives it another chance to run next time they view the order.
        add_action( 'woocommerce_view_order', array( $this, 'handle_view_order' ), 10, 1 );
    }

    function set_custom_cart_item_price( $cart ) {

        if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
            return;
        }

        foreach ( $cart->get_cart() as $cart_item ) {

            if ( isset( $cart_item['custom_price'] ) ) {

                $cart_item['data']->set_price( (float) $cart_item['custom_price'] );

            }
        }
    }

    // $values is the cart item's own data array (what CGC_Rest_API::add_to_cart()
    // built as $cart_item_data) - guarded on card_id so this is a no-op for
    // any other, unrelated product in the same order. No $item->save() call
    // needed - WooCommerce's own checkout flow persists the item afterward.
    function copy_cart_item_data_to_order_item( $item, $cart_item_key, $values, $order ) {
        if ( empty( $values['card_id'] ) ) {
            return;
        }

        $item->add_meta_data( 'card_id', $values['card_id'], true );
        $item->add_meta_data( 'title', $values['title'] ?? '', true );
        $item->add_meta_data( 'password', $values['password'] ?? '', true );

        foreach ( (array) ( $values['addon_meta'] ?? array() ) as $label => $value ) {
            $item->add_meta_data( $label, $value, true );
        }
    }

    // card_id/title/password are internal bookkeeping, not something a
    // customer should see on their order-received page/emails - addon
    // selections (added as plain, non-prefixed meta above) are left
    // visible, since those are meant to display exactly like a normal
    // WooCommerce Product Add-Ons purchase would.
    function hide_internal_order_itemmeta( $hidden ) {
        $hidden[] = 'card_id';
        $hidden[] = 'title';
        $hidden[] = 'password';
        return $hidden;
    }

    function gift_card_approve( $order_id ) {
        if ( ! $order_id ) {
            return;
        }

        $order = wc_get_order( $order_id );
        if ( ! $order ) {
            return;
        }

        // Showing the card (when there's no email field) should happen on
        // every page view, not just once - the buyer may refresh or revisit.
        foreach ( $order->get_items() as $item ) {
            $card_id = $item->get_meta( 'card_id' );
            if ( ! $card_id ) {
                continue;
            }

            $this->render_thankyou_card( $card_id );

            // All three run regardless of _cgc_has_email_field - the
            // regenerated attachment is the durable "final card" record the
            // email below needs either way, not just an on-page display
            // concern.
            $this->maybe_generate_coupon( $card_id, $order, $item );
            $this->maybe_enqueue_rerender( $card_id );
            $this->maybe_send_gift_card_email( $card_id );
        }

        // Reserved for the future "email the card to recipients" flow, so
        // that part only ever fires once even if the thank-you page reloads.
        if ( ! $order->get_meta( '_thankyou_action_done' ) ) {
            $order->update_meta_data( '_thankyou_action_done', true );
            $order->save();
        }
    }

    // woocommerce_payment_complete - the reliable "actually paid" signal
    // (see the constructor's comment). Coupon generation is idempotent
    // (checks _cgc_coupon_code first), so this is safe to also reach via
    // gift_card_approve() on a normal same-session checkout.
    function handle_payment_complete( $order_id ) {
        if ( ! $order_id ) {
            return;
        }

        $order = wc_get_order( $order_id );
        if ( ! $order ) {
            return;
        }

        foreach ( $order->get_items() as $item ) {
            $card_id = $item->get_meta( 'card_id' );
            if ( $card_id ) {
                $this->maybe_generate_coupon( $card_id, $order, $item );
                // Designs with no {coupon_code}/{expire_date} tokens don't
                // need the async re-render step - their attachment is
                // already final, so the email can go out immediately here
                // rather than waiting for a thank-you-page view.
                $this->maybe_send_gift_card_email( $card_id );
            }
        }
    }

    // Fallback trigger for the re-render step (see the constructor's
    // comment) - fires on My Account -> Orders -> View, a different
    // template/hook than the immediate checkout/order-received page.
    function handle_view_order( $order_id ) {
        if ( ! $order_id ) {
            return;
        }

        $order = wc_get_order( $order_id );
        if ( ! $order ) {
            return;
        }

        foreach ( $order->get_items() as $item ) {
            $card_id = $item->get_meta( 'card_id' );
            if ( ! $card_id ) {
                continue;
            }

            $this->maybe_generate_coupon( $card_id, $order, $item );
            $this->maybe_enqueue_rerender( $card_id );
            $this->maybe_send_gift_card_email( $card_id );
        }
    }

    // Creates a real, spendable WooCommerce coupon worth what the customer
    // actually paid for this gift card - only when the design that created
    // it actually uses a {coupon_code} token (_cgc_uses_coupon_token, set in
    // class-cgc-rest-api.php's add_to_cart()), and only once payment is
    // confirmed. Idempotent: safe to call from multiple hooks/page views.
    function maybe_generate_coupon( $card_id, $order, $item ) {
        // Real coupon generation is a Pro feature - a free site's design can
        // still contain a literal {coupon_code} token; render_form()'s
        // existing $card_placeholders['coupon_code'] cosmetic fallback just
        // keeps showing "COUPON CODE" placeholder text forever, since no
        // real code is ever generated here to substitute in.
        if ( ! cgc_is_pro_active() ) {
            return;
        }

        if ( ! get_post_meta( $card_id, '_cgc_uses_coupon_token', true ) ) {
            return;
        }

        if ( get_post_meta( $card_id, '_cgc_coupon_code', true ) ) {
            return; // already generated
        }

        if ( ! $order->has_status( array( 'processing', 'completed' ) ) ) {
            return; // not actually paid yet
        }

        if ( ! class_exists( 'WC_Coupon' ) ) {
            return;
        }

        $amount = (float) $item->get_total();
        if ( $amount <= 0 ) {
            return; // don't create a worthless/free coupon
        }

        $code = '';
        for ( $attempt = 0; $attempt < 5; $attempt++ ) {
            $candidate = 'GIFT-' . strtoupper( wp_generate_password( 8, false, false ) );
            if ( ! wc_get_coupon_id_by_code( $candidate ) ) {
                $code = $candidate;
                break;
            }
        }

        if ( ! $code ) {
            // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- intentional operator-facing log for a real failure path (retries exhausted), not leftover debug code.
            error_log( 'CGC: coupon code collision exhausted retries for card ' . $card_id );
            return;
        }

        $end_date = get_post_meta( $card_id, '_cgc_end_date', true );

        $coupon = new WC_Coupon();
        $coupon->set_code( $code );
        $coupon->set_amount( $amount );
        $coupon->set_discount_type( 'fixed_cart' );
        $coupon->set_individual_use( true );
        $coupon->set_usage_limit( 1 );
        if ( $end_date ) {
            $coupon->set_date_expires( strtotime( $end_date ) );
        }
        $coupon->save();

        update_post_meta( $card_id, '_cgc_coupon_code', $code );
        update_post_meta( $card_id, '_cgc_coupon_id', $coupon->get_id() );
    }

    // Enqueues a lightweight, invisible bootstrap that mounts the existing
    // Fabric.js canvas (same component the design editor/checkout use),
    // loads this specific purchase's stored design JSON, substitutes the
    // real {coupon_code}/{expire_date} values, and POSTs the finalized
    // image back to /finalize_card_render - only when the design actually
    // needs it (_cgc_needs_rerender) and it hasn't already been done.
    function maybe_enqueue_rerender( $card_id ) {
        if ( ! get_post_meta( $card_id, '_cgc_needs_rerender', true ) ) {
            return;
        }

        if ( get_post_meta( $card_id, '_cgc_rerender_done', true ) ) {
            return;
        }

        $design_json = get_post_meta( $card_id, '_cgc_design_json', true );
        if ( ! $design_json ) {
            return;
        }

        $asset_file = include( CGC_PLUGIN_PATH . 'build/thankyou-render/index.asset.php' );
        if ( ! is_array( $asset_file ) ) {
            return;
        }

        $token = wp_generate_password( 32, false );
        update_post_meta( $card_id, '_cgc_rerender_token', $token );

        $width = (int) get_post_meta( $card_id, '_cgc_canvas_width', true );
        $height = (int) get_post_meta( $card_id, '_cgc_canvas_height', true );
        $end_date = get_post_meta( $card_id, '_cgc_end_date', true );
        $coupon_code = get_post_meta( $card_id, '_cgc_coupon_code', true );

        // A design can use {expire_date} without {coupon_code} - only wait
        // for a coupon to exist when the design actually references one.
        $uses_coupon_token = get_post_meta( $card_id, '_cgc_uses_coupon_token', true );
        if ( $uses_coupon_token && ! $coupon_code ) {
            return; // coupon not generated yet (payment not confirmed) - try again on the next page view
        }

        wp_enqueue_script( 'cgc-thankyou-render', CGC_PLUGIN_URL . 'build/thankyou-render/index.js', $asset_file['dependencies'], CGC_PLUGIN_VERSION, true );
        wp_set_script_translations( 'cgc-thankyou-render', 'gift-card-studio', CGC_PLUGIN_PATH . 'languages' );
        wp_enqueue_style( 'cgc-thankyou-render', CGC_PLUGIN_URL . 'build/thankyou-render.css', array(), CGC_PLUGIN_VERSION );
        wp_enqueue_style( 'cgc-fonts', CGC_PLUGIN_URL . 'assets/fonts.css', array(), CGC_PLUGIN_VERSION );

        wp_localize_script( 'cgc-thankyou-render', 'appData', array(
            'rest_url' => rest_url( 'cgc/v2' ),
            'nonce' => wp_create_nonce( 'wp_rest' ),
            'card_id' => (int) $card_id,
            'rerender_token' => $token,
            'json' => $design_json,
            'width' => $width,
            'height' => $height,
            'coupon_code' => $coupon_code ?: '',
            'expire_date' => $end_date ? date_i18n( get_option( 'date_format' ), strtotime( $end_date ) ) : '',
        ) );

        echo '<div class="cgc-canvas-mount" data-card-id="' . esc_attr( $card_id ) . '" style="display:none"></div>';
    }

    // Emails the finalized card image to every recipient address the buyer
    // entered in the form's "emails" field. Idempotent (_cgc_email_sent) so
    // it's safe to call from every hook above on every page view. Waits for
    // maybe_enqueue_rerender()'s async re-render to actually finish before
    // sending when the design needs one (real {coupon_code}/{expire_date}
    // values) - class-cgc-rest-api.php's finalize_card_render() calls this
    // again once that completes, so the recipient always gets the final
    // image, never one still showing a literal placeholder token.
    function maybe_send_gift_card_email( $card_id ) {
        if ( ! get_post_meta( $card_id, '_cgc_has_email_field', true ) ) {
            return; // this design has no recipient-email field at all
        }

        if ( get_post_meta( $card_id, '_cgc_email_sent', true ) ) {
            return; // already sent
        }

        if ( get_post_meta( $card_id, '_cgc_needs_rerender', true ) && ! get_post_meta( $card_id, '_cgc_rerender_done', true ) ) {
            return; // final image not ready yet - try again once it is
        }

        $post = get_post( $card_id );
        if ( ! $post ) {
            return;
        }

        $data = unserialize( base64_decode( $post->post_content ) );
        $emails = ! empty( $data['emails'] ) ? array_filter( array_map( 'sanitize_email', (array) $data['emails'] ) ) : array();

        if ( ! $emails ) {
            return;
        }

        $attach_id = get_post_meta( $card_id, '_cgc_attach_id', true );
        $image_url = $attach_id ? wp_get_attachment_url( $attach_id ) : '';
        $image_path = $attach_id ? get_attached_file( $attach_id ) : '';

        if ( ! $image_url ) {
            return;
        }

        $from_name = ! empty( $data['from'] ) ? $data['from'] : get_bloginfo( 'name' );

        /* translators: %s: name of the gift card sender */
        $subject = sprintf( esc_html__( '%s sent you a gift card!', 'gift-card-studio' ), $from_name );

        $message = ! empty( $data['message'] ) ? '<p>' . esc_html( $data['message'] ) . '</p>' : '';

        /* translators: %s: name of the gift card sender */
        $body = '<p>' . sprintf( esc_html__( '%s sent you a gift card.', 'gift-card-studio' ), esc_html( $from_name ) ) . '</p>'
            . $message
            . '<p><img src="' . esc_url( $image_url ) . '" alt="' . esc_attr__( 'Your gift card', 'gift-card-studio' ) . '" style="max-width:100%;height:auto;" /></p>';

        $headers = array( 'Content-Type: text/html; charset=UTF-8' );
        $attachments = $image_path ? array( $image_path ) : array();

        $sent_to_any = false;
        foreach ( $emails as $recipient ) {
            if ( wp_mail( $recipient, $subject, $body, $headers, $attachments ) ) {
                $sent_to_any = true;
            }
        }

        if ( $sent_to_any ) {
            update_post_meta( $card_id, '_cgc_email_sent', true );
        }
    }

    // When the form that created this card had no recipient-email field
    // configured, the buyer is shown their personalized card directly here
    // instead of it being emailed to recipients.
    function render_thankyou_card( $card_id ) {
        $has_email_field = get_post_meta( $card_id, '_cgc_has_email_field', true );

        if ( $has_email_field ) {
            return;
        }

        $attach_id = get_post_meta( $card_id, '_cgc_attach_id', true );
        $image_url = $attach_id ? wp_get_attachment_url( $attach_id ) : '';

        if ( ! $image_url ) {
            return;
        }
        ?>
        <div class="cgc-thankyou-card">
            <h2><?php esc_html_e( 'Your Gift Card', 'gift-card-studio' ); ?></h2>
            <img src="<?php echo esc_url( $image_url ); ?>" alt="<?php esc_attr_e( 'Your gift card', 'gift-card-studio' ); ?>" style="max-width:100%;height:auto;" />
            <p>
                <a class="button" href="<?php echo esc_url( $image_url ); ?>" download>
                    <?php esc_html_e( 'Download Your Gift Card', 'gift-card-studio' ); ?>
                </a>
            </p>
        </div>
        <?php
    }

}

// Initialize the class
global $cgc_woocommerce;
$cgc_woocommerce = new CGC_WooCommerce();
?>
