<?php

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

class AIEO_CAPI_Sender {

	const GRAPH_VERSION   = 'v21.0';
	const ASYNC_HOOK      = 'aieo_capi_send_purchase';
	const EVENT_ID_META   = '_aieo_fb_event_id';
	const SENT_META       = '_aieo_capi_purchase_sent';
	const PLATFORM        = 'fb';

	
	private static $instance = null;

	
	private static $enqueued = array();

	public static function instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	public function register_hooks() {
		
		
		add_action( 'woocommerce_checkout_create_order', array( $this, 'snapshot_identifiers' ), 20, 2 );
		add_action( 'woocommerce_store_api_checkout_order_processed', array( $this, 'snapshot_identifiers_block' ), 20, 1 );

		
		
		add_action( 'woocommerce_payment_complete', array( $this, 'maybe_send' ), 20, 1 );
		add_action( 'woocommerce_order_status_processing', array( $this, 'maybe_send' ), 20, 1 );
		add_action( 'woocommerce_order_status_completed', array( $this, 'maybe_send' ), 20, 1 );

		
		add_action( self::ASYNC_HOOK, array( $this, 'send_purchase' ), 10, 1 );
	}

	
	
	

	
	public static function config() {
		if ( class_exists( 'AIEO_DMM_Conversions' ) && method_exists( 'AIEO_DMM_Conversions', 'get_config' ) ) {
			return AIEO_DMM_Conversions::get_config();
		}
		
		$o = get_option( 'aieo_dmm_conversions', array() );
		return is_array( $o ) ? $o : array();
	}

	
	public static function resolve_credentials( array $config ) {
		$pixel_id = isset( $config['fb_pixel_id'] ) ? trim( (string) $config['fb_pixel_id'] ) : '';
		$token    = isset( $config['fb_access_token'] ) ? trim( (string) $config['fb_access_token'] ) : '';

		if ( ( '' === $pixel_id || '' === $token ) && function_exists( '\\PixelYourSite\\Facebook' ) ) {
			try {
				$fb     = \PixelYourSite\Facebook();
				$ids    = method_exists( $fb, 'getPixelIDs' ) ? (array) $fb->getPixelIDs() : array();
				$tokens = method_exists( $fb, 'getApiTokens' ) ? (array) $fb->getApiTokens() : array();
				if ( '' === $pixel_id && ! empty( $ids ) ) {
					$pixel_id = (string) reset( $ids );
				}
				if ( '' === $token ) {
					if ( '' !== $pixel_id && isset( $tokens[ $pixel_id ] ) ) {
						$token = (string) $tokens[ $pixel_id ];
					} elseif ( ! empty( $tokens ) ) {
						$token = (string) reset( $tokens );
					}
				}
			} catch ( \Throwable $e ) {
				
			}
		}

		return array( 'pixel_id' => $pixel_id, 'token' => $token );
	}

	
	private static function server_enabled( array $config ) {
		$mode = isset( $config['fb_mode'] ) ? $config['fb_mode'] : 'off';
		return in_array( $mode, array( 'capi', 'both' ), true );
	}

	
	private static function consent_blocks() {
		if ( ! class_exists( 'AIEO_Session_Tracker' ) ) {
			return false;
		}
		
		
		if ( method_exists( 'AIEO_Session_Tracker', 'is_marketing_consent_blocking' ) ) {
			return AIEO_Session_Tracker::is_marketing_consent_blocking();
		}
		if ( method_exists( 'AIEO_Session_Tracker', 'is_consent_blocking_writes' ) ) {
			return AIEO_Session_Tracker::is_consent_blocking_writes();
		}
		return false;
	}

	
	
	

	
	public function snapshot_identifiers( $order, $data = array() ) {
		unset( $data );
		$this->do_snapshot( $order );
	}

	
	public function snapshot_identifiers_block( $order ) {
		$this->do_snapshot( $order );
	}

	
	private function do_snapshot( $order ) {
		if ( ! is_a( $order, 'WC_Order' ) ) {
			return;
		}
		$fbp = isset( $_COOKIE['_fbp'] ) ? sanitize_text_field( wp_unslash( $_COOKIE['_fbp'] ) ) : '';
		$fbc = isset( $_COOKIE['_fbc'] ) ? sanitize_text_field( wp_unslash( $_COOKIE['_fbc'] ) ) : '';
		$sid = '';
		foreach ( array( 'aieo_attr_sid', 'aieo_session_id' ) as $ck ) {
			if ( ! empty( $_COOKIE[ $ck ] ) ) {
				$sid = sanitize_text_field( wp_unslash( $_COOKIE[ $ck ] ) );
				break;
			}
		}
		if ( '' !== $fbp ) { $order->update_meta_data( '_aieo_fbp', $fbp ); }
		if ( '' !== $fbc ) { $order->update_meta_data( '_aieo_fbc', $fbc ); }
		if ( '' !== $sid ) { $order->update_meta_data( '_aieo_attr_sid', $sid ); }
		
	}

	
	public function maybe_send( $order_id ) {
		$order_id = (int) $order_id;
		if ( $order_id <= 0 || ! function_exists( 'wc_get_order' ) ) {
			return;
		}
		$config = self::config();
		if ( ! self::server_enabled( $config ) ) {
			return;
		}
		if ( self::consent_blocks() ) {
			return;
		}
		$creds = self::resolve_credentials( $config );
		if ( '' === $creds['pixel_id'] || '' === $creds['token'] ) {
			return; 
		}
		$order = wc_get_order( $order_id );
		if ( ! $order ) {
			return;
		}
		if ( $order->get_meta( self::SENT_META ) ) {
			return; 
		}
		
		
		if ( isset( self::$enqueued[ $order_id ] ) ) {
			return;
		}
		self::$enqueued[ $order_id ] = true;

		
		
		
		
		self::get_or_mint_event_id( $order );

		
		
		
		
		if ( function_exists( 'aieo_server_event_dispatch' ) ) {
			aieo_server_event_dispatch( self::ASYNC_HOOK, $order_id );
		} elseif ( function_exists( 'as_enqueue_async_action' ) ) {
			as_enqueue_async_action( self::ASYNC_HOOK, array( $order_id ), 'aieo-server-events' );
		} else {
			wp_schedule_single_event( time() + 1, self::ASYNC_HOOK, array( $order_id ) );
		}
	}

	
	public static function get_or_mint_event_id( $order ) {
		if ( ! is_a( $order, 'WC_Order' ) ) {
			return '';
		}
		$eid = (string) $order->get_meta( self::EVENT_ID_META );
		if ( '' === $eid ) {
			$eid = function_exists( 'wp_generate_uuid4' )
				? wp_generate_uuid4()
				: ( class_exists( 'AIEO_Attribution' ) && method_exists( 'AIEO_Attribution', 'generate_uuid_v4' )
					? AIEO_Attribution::generate_uuid_v4()
					: sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x', wp_rand( 0, 0xffff ), wp_rand( 0, 0xffff ), wp_rand( 0, 0xffff ), wp_rand( 0, 0x0fff ) | 0x4000, wp_rand( 0, 0x3fff ) | 0x8000, wp_rand( 0, 0xffff ), wp_rand( 0, 0xffff ), wp_rand( 0, 0xffff ) ) );
			$order->update_meta_data( self::EVENT_ID_META, $eid );
			$order->save();
		}
		return $eid;
	}

	
	
	

	
	public function send_purchase( $order_id ) {
		$order_id = (int) $order_id;
		if ( $order_id <= 0 || ! function_exists( 'wc_get_order' ) ) {
			return;
		}
		$config = self::config();
		if ( ! self::server_enabled( $config ) || self::consent_blocks() ) {
			return;
		}
		$creds = self::resolve_credentials( $config );
		if ( '' === $creds['pixel_id'] || '' === $creds['token'] ) {
			return;
		}
		$order = wc_get_order( $order_id );
		if ( ! $order ) {
			return;
		}
		$eid = (string) $order->get_meta( self::EVENT_ID_META );
		if ( '' === $eid ) {
			return;
		}

		
		if ( $this->already_sent( $eid ) ) {
			return;
		}

		$payload   = $this->build_purchase_payload( $order, $config );
		$test_code = isset( $config['fb_test_event_code'] ) ? trim( (string) $config['fb_test_event_code'] ) : '';
		$body      = array( 'data' => array( $payload ) );
		if ( '' !== $test_code ) {
			$body['test_event_code'] = $test_code;
		}

		
		
		
		
		
		
		$dpo = apply_filters( 'aieo_capi_data_processing_options', array(), $order );
		$body['data_processing_options'] = is_array( $dpo ) ? array_values( $dpo ) : array();
		if ( ! empty( $body['data_processing_options'] ) ) {
			$body['data_processing_options_country'] = (int) apply_filters( 'aieo_capi_data_processing_country', 0, $order );
			$body['data_processing_options_state']   = (int) apply_filters( 'aieo_capi_data_processing_state', 0, $order );
		}

		$url = sprintf(
			'https://graph.facebook.com/%s/%s/events?access_token=%s',
			self::GRAPH_VERSION,
			rawurlencode( $creds['pixel_id'] ),
			rawurlencode( $creds['token'] )
		);

		
		
		
		
		
		$blocking = ( '' !== $test_code ) || (bool) apply_filters( 'aieo_server_event_blocking', true, self::PLATFORM );
		$resp = wp_remote_post( $url, array(
			'timeout'  => 6,
			'blocking' => $blocking,
			'headers'  => array( 'Content-Type' => 'application/json' ),
			'body'     => wp_json_encode( $body ),
		) );

		if ( $blocking ) {
			$sent = $this->log_result( $order, $eid, $payload, $resp, ( '' !== $test_code ) );
			
			
			if ( $sent && '' === $test_code ) {
				$order->update_meta_data( self::SENT_META, 1 );
				$order->save();
			}
		} elseif ( function_exists( 'aieo_server_event_log_dispatched' ) ) {
			aieo_server_event_log_dispatched( self::PLATFORM, 'Purchase', $order, $eid );
		}
	}

	
	public function build_purchase_payload( $order, array $config = array() ) {
		$ids = $this->resolve_identifiers( $order );

		$content_ids = array();
		$contents    = array();
		$num_items   = 0;
		foreach ( $order->get_items() as $item ) {
			if ( ! is_a( $item, 'WC_Order_Item_Product' ) ) {
				continue;
			}
			$pid = $item->get_variation_id() ? (int) $item->get_variation_id() : (int) $item->get_product_id();
			$qty = (int) $item->get_quantity();
			$num_items += $qty;
			$content_ids[] = (string) $pid;
			$contents[]    = array(
				'id'         => (string) $pid,
				'quantity'   => $qty,
				'item_price' => $qty > 0 ? round( (float) $item->get_total() / $qty, 2 ) : (float) $item->get_total(),
			);
		}

		$created = $order->get_date_created();
		$event_time = $created ? $created->getTimestamp() : time();

		$source_url = method_exists( $order, 'get_checkout_order_received_url' )
			? $order->get_checkout_order_received_url()
			: home_url( '/' );

		$payload = array(
			'event_name'       => 'Purchase',
			'event_time'       => (int) $event_time,
			'event_id'         => (string) $order->get_meta( self::EVENT_ID_META ),
			'action_source'    => 'website',
			'event_source_url' => $source_url,
			'user_data'        => AIEO_PII_Hasher::build_user_data( $order, $ids ),
			'custom_data'      => array(
				'currency'     => $order->get_currency(),
				'value'        => (float) $order->get_total(),
				'num_items'    => $num_items,
				'content_type' => 'product',
				'content_ids'  => $content_ids,
				'contents'     => $contents,
			),
		);

		return $payload;
	}

	
	private function resolve_identifiers( $order ) {
		$fbp = (string) $order->get_meta( '_aieo_fbp' );
		$fbc = (string) $order->get_meta( '_aieo_fbc' );
		$sid = (string) $order->get_meta( '_aieo_attr_sid' );

		if ( '' === $fbc && '' !== $sid ) {
			global $wpdb;
			$statics = $wpdb->prefix . 'aieo_sessions_statics';
			
			$row = $wpdb->get_row( $wpdb->prepare(
				"SELECT entry_click_id, entry_channel, first_timestamp FROM `{$statics}` WHERE session_id = %s LIMIT 1",
				$sid
			), ARRAY_A );
			if ( $row && ! empty( $row['entry_click_id'] ) && 'paid_facebook' === ( $row['entry_channel'] ?? '' ) ) {
				$ts  = ! empty( $row['first_timestamp'] ) ? (int) $row['first_timestamp'] : (int) ( time() * 1000 );
				$fbc = 'fb.1.' . $ts . '.' . $row['entry_click_id'];
			}
		}

		return array(
			'fbp'         => $fbp,
			'fbc'         => $fbc,
			'external_id' => $sid,
		);
	}

	
	
	

	private function table() {
		global $wpdb;
		return $wpdb->prefix . 'aieo_server_events_log';
	}

	private function table_ok() {
		global $wpdb;
		static $ok = null;
		if ( null === $ok ) {
			$t  = $this->table();
			$ok = ( (string) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $t ) ) === $t );
		}
		return $ok;
	}

	private function already_sent( $event_id ) {
		if ( ! $this->table_ok() ) {
			return false;
		}
		global $wpdb;
		$t = $this->table();
		
		return (bool) $wpdb->get_var( $wpdb->prepare(
			"SELECT 1 FROM `{$t}` WHERE event_id = %s AND platform = %s AND status = 'sent' LIMIT 1",
			$event_id, self::PLATFORM
		) );
	}

	
	private function log_result( $order, $event_id, $payload, $resp, $test_mode ) {
		if ( ! $this->table_ok() ) {
			
			$code = is_wp_error( $resp ) ? 0 : (int) wp_remote_retrieve_response_code( $resp );
			return ( $code >= 200 && $code < 300 );
		}
		global $wpdb;
		$t = $this->table();

		$http_code   = null;
		$fbtrace     = null;
		$resp_body   = '';
		$status      = 'failed';
		$err         = null;

		if ( is_wp_error( $resp ) ) {
			$err = substr( $resp->get_error_message(), 0, 255 );
		} else {
			$http_code = (int) wp_remote_retrieve_response_code( $resp );
			$resp_body = (string) wp_remote_retrieve_body( $resp );
			$decoded   = json_decode( $resp_body, true );
			if ( is_array( $decoded ) && isset( $decoded['fbtrace_id'] ) ) {
				$fbtrace = substr( (string) $decoded['fbtrace_id'], 0, 64 );
			}
			$status = ( $http_code >= 200 && $http_code < 300 ) ? 'sent' : 'failed';
			if ( 'failed' === $status && is_array( $decoded ) && isset( $decoded['error']['message'] ) ) {
				$err = substr( (string) $decoded['error']['message'], 0, 255 );
			}
		}

		$now = current_time( 'mysql', true );
		$cd  = isset( $payload['custom_data'] ) ? $payload['custom_data'] : array();

		
		$wpdb->query( $wpdb->prepare(
			"INSERT INTO `{$t}`
				(event_id, platform, event_name, order_id, session_id, user_id, status, http_code, fbtrace_id,
				 attempts, request_payload, response_body, error_message, value, currency, test_mode, created_at, sent_at)
			 VALUES (%s, %s, %s, %d, %s, %d, %s, %d, %s, 1, %s, %s, %s, %f, %s, %d, %s, %s)
			 ON DUPLICATE KEY UPDATE
				status = VALUES(status), http_code = VALUES(http_code), fbtrace_id = VALUES(fbtrace_id),
				attempts = attempts + 1, request_payload = VALUES(request_payload), response_body = VALUES(response_body),
				error_message = VALUES(error_message), sent_at = VALUES(sent_at)",
			$event_id,
			self::PLATFORM,
			'Purchase',
			(int) $order->get_id(),
			(string) $order->get_meta( '_aieo_attr_sid' ),
			(int) $order->get_customer_id(),
			$status,
			null === $http_code ? 0 : $http_code,
			null === $fbtrace ? '' : $fbtrace,
			wp_json_encode( $payload ),
			substr( $resp_body, 0, 4000 ),
			null === $err ? '' : $err,
			isset( $cd['value'] ) ? (float) $cd['value'] : 0,
			isset( $cd['currency'] ) ? (string) $cd['currency'] : '',
			$test_mode ? 1 : 0,
			$now,
			'sent' === $status ? $now : null
		) );

		return 'sent' === $status;
	}
}
