<?php
/**
 * Tracks abandoned carts at checkout.
 *
 * @package SuiteMkt3k
 */

namespace SuiteMkt3k\Modules\Cart_Recovery;

use SuiteMkt3k\Compat\WooCommerce_Check;
use SuiteMkt3k\Installer;
use SuiteMkt3k\Security\Nonces;
use SuiteMkt3k\Security\Rate_Limiter;
use SuiteMkt3k\Utils\Sanitizer;
use SuiteMkt3k\Utils\Validator;

defined( 'ABSPATH' ) || exit;
// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching

final class Cart_Tracker {

	public static function register(): void {
		add_action( 'woocommerce_checkout_update_order_review', array( __CLASS__, 'capture_from_review' ) );
		add_action( 'woocommerce_after_checkout_billing_form', array( __CLASS__, 'inject_capture_assets' ) );
		// Close open carts after checkout; true recoveries are handled by Cart_Recovery_Manager.
		add_action( 'woocommerce_thankyou', array( __CLASS__, 'on_thankyou' ) );
		add_action( 'wp_ajax_suitemkt3k_capture_cart', array( __CLASS__, 'ajax_capture' ) );
		add_action( 'wp_ajax_nopriv_suitemkt3k_capture_cart', array( __CLASS__, 'ajax_capture' ) );
	}

	public static function table(): string {
		return Installer::table( 'abandoned_carts' );
	}

	public static function capture_from_review( $post_data ): void {
		if ( ! WooCommerce_Check::is_active() ) {
			return;
		}
		parse_str( (string) $post_data, $fields );
		$email = Sanitizer::email( $fields['billing_email'] ?? '' );
		if ( '' === $email ) {
			return;
		}
		self::persist(
			array(
				'email'   => $email,
				'name'    => trim( ( $fields['billing_first_name'] ?? '' ) . ' ' . ( $fields['billing_last_name'] ?? '' ) ),
				'phone'   => $fields['billing_phone'] ?? '',
				'billing' => array(
					'billing_first_name' => $fields['billing_first_name'] ?? '',
					'billing_last_name'  => $fields['billing_last_name'] ?? '',
					'billing_company'    => $fields['billing_company'] ?? '',
					'billing_address_1'  => $fields['billing_address_1'] ?? '',
					'billing_address_2'  => $fields['billing_address_2'] ?? '',
					'billing_city'       => $fields['billing_city'] ?? '',
					'billing_postcode'   => $fields['billing_postcode'] ?? '',
					'billing_country'    => $fields['billing_country'] ?? '',
					'billing_state'      => $fields['billing_state'] ?? '',
					'billing_phone'      => $fields['billing_phone'] ?? '',
					'billing_email'      => $email,
				),
			)
		);
	}

	public static function inject_capture_assets(): void {
		if ( is_admin() || ! WooCommerce_Check::is_active() ) {
			return;
		}
		wp_enqueue_script(
			'suitemkt3k-cart-capture',
			SUITEMKT3K_URL . 'assets/frontend/js/cart-capture.js',
			array(),
			SUITEMKT3K_VERSION,
			true
		);
		wp_localize_script(
			'suitemkt3k-cart-capture',
			'SuiteMkt3kCartCapture',
			array(
				'ajaxUrl' => esc_url_raw( admin_url( 'admin-ajax.php' ) ),
				'nonce'   => Nonces::create( Nonces::PUBLIC_FORM ),
			)
		);
	}

	public static function ajax_capture(): void {
		$nonce = Sanitizer::post( 'nonce', 'text' );
		if ( ! Nonces::verify_admin( $nonce, Nonces::PUBLIC_FORM ) ) {
			wp_send_json_error( array( 'message' => 'invalid_nonce' ), 403 );
		}
		$visitor = Rate_Limiter::visitor_id();
		if ( ! Rate_Limiter::check( 'cart_capture', $visitor, 20, 60 ) ) {
			wp_send_json_error( array( 'message' => 'rate_limited' ), 429 );
		}
		$email = Sanitizer::email( Sanitizer::post( 'email', 'email' ) );
		if ( ! Validator::is_valid_email( $email ) ) {
			wp_send_json_error( array( 'message' => 'invalid_email' ), 400 );
		}
		self::persist(
			array(
				'email' => $email,
				'name'  => Sanitizer::text( Sanitizer::post( 'name', 'text' ), 190 ),
				'phone' => Sanitizer::text( Sanitizer::post( 'phone', 'text' ), 60 ),
			)
		);
		wp_send_json_success( array( 'ok' => true ) );
	}

	public static function persist( array $args ): void {
		if ( ! WooCommerce_Check::is_active() ) {
			return;
		}
		if ( ! function_exists( 'WC' ) || ! WC()->cart ) {
			return;
		}
		$cart = WC()->cart;
		if ( ! $cart || $cart->is_empty() ) {
			return;
		}

		global $wpdb;
		$session_key = WC()->session ? WC()->session->get_customer_id() : '';
		$cart_hash   = hash( 'sha256', ( $args['email'] ?? '' ) . '|' . $session_key . '|' . get_option( 'suitemkt3k_secret_key' ) );

		$items = array();
		foreach ( $cart->get_cart() as $key => $item ) {
			$items[] = array(
				'product_id'   => (int) ( $item['product_id'] ?? 0 ),
				'variation_id' => (int) ( $item['variation_id'] ?? 0 ),
				'qty'          => (int) ( $item['quantity'] ?? 1 ),
			);
		}

		$payload = array(
			'cart_hash'        => $cart_hash,
			'wc_session_key'   => $session_key,
			'user_id'          => get_current_user_id() ?: null,
			'email'            => Sanitizer::email( $args['email'] ?? '' ),
			'name'             => Sanitizer::text( $args['name'] ?? '', 190 ),
			'phone'            => Sanitizer::text( $args['phone'] ?? '', 60 ),
			'cart_items_json'  => wp_json_encode( $items ),
			'billing_json'     => ! empty( $args['billing'] ) && is_array( $args['billing'] )
				? wp_json_encode( Sanitizer::recursive_text( $args['billing'] ) )
				: null,
			'total'            => (float) $cart->get_total( 'edit' ),
			'currency'         => function_exists( 'get_woocommerce_currency' ) ? get_woocommerce_currency() : '',
			'recovery_token'   => bin2hex( random_bytes( 32 ) ),
			'last_activity_at' => current_time( 'mysql', true ),
			'status'           => 'pending',
		);

		$existing = $wpdb->get_row(
			$wpdb->prepare( 'SELECT id, status FROM %i WHERE cart_hash = %s', self::table(), $cart_hash ),
			ARRAY_A
		);
		if ( $existing ) {
			unset( $payload['recovery_token'] );
			// Never downgrade emailed/recovered/closed carts back to pending during checkout updates.
			$locked = array( 'emailed', 'recovered', 'completed', 'deleted', 'expired' );
			if ( in_array( (string) ( $existing['status'] ?? '' ), $locked, true ) ) {
				unset( $payload['status'] );
			}
			$wpdb->update( self::table(), $payload, array( 'id' => (int) $existing['id'] ) );
		} else {
			$wpdb->insert( self::table(), $payload );
		}
	}

	/**
	 * After a paid checkout, close leftover open carts / mark true recoveries.
	 * True "recovered" status is assigned only via Cart_Recovery_Manager attribution.
	 */
	public static function on_thankyou( int $order_id ): void {
		if ( ! WooCommerce_Check::is_active() || ! $order_id || ! function_exists( 'wc_get_order' ) ) {
			return;
		}
		$order = wc_get_order( $order_id );
		if ( ! $order || ! $order->has_status( array( 'processing', 'completed' ) ) ) {
			return;
		}
		// Fallback when order-status hooks already ran — method is idempotent.
		Cart_Recovery_Manager::maybe_mark_recovered( $order_id );
	}
}
