<?php
/**
 * API Client class for Voice Logica AI for WooCommerce plugin
 *
 * @package VoiceLogica_AI_WooCommerce
 */

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

/**
 * Class VoiceLogica_AI_API_Client
 */
class VoiceLogica_AI_API_Client {

	/**
	 * Option name for webhook secret
	 */
	const OPTION_WEBHOOK_SECRET = 'voicelogica_ai_webhook_secret';

	/**
	 * Option name for API key ID
	 */
	const OPTION_API_KEY_ID = 'voicelogica_ai_api_key_id';

	/**
	 * Option name for API consumer key
	 */
	const OPTION_API_CONSUMER_KEY = 'voicelogica_ai_api_consumer_key';

	/**
	 * Option name for API consumer secret
	 */
	const OPTION_API_CONSUMER_SECRET = 'voicelogica_ai_api_consumer_secret';

	/**
	 * Encrypt sensitive data
	 *
	 * @param string $data Data to encrypt.
	 * @return string Encrypted data.
	 */
	private function encrypt_data( $data ) {
		if ( ! function_exists( 'openssl_encrypt' ) ) {
			// Fallback: use base64 encoding if OpenSSL not available (not secure, but better than plain text).
			// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- Benign: encryption fallback for API credentials.
			return base64_encode( $data );
		}

		$key       = $this->get_encryption_key();
		$iv        = openssl_random_pseudo_bytes( openssl_cipher_iv_length( 'AES-256-CBC' ) );
		$encrypted = openssl_encrypt( $data, 'AES-256-CBC', $key, 0, $iv );
		// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- Benign: binary data encoding for storage.
		return base64_encode( $iv . $encrypted );
	}

	/**
	 * Decrypt sensitive data
	 *
	 * @param string $encrypted_data Encrypted data.
	 * @return string|false Decrypted data or false on failure.
	 */
	private function decrypt_data( $encrypted_data ) {
		if ( empty( $encrypted_data ) ) {
			return false;
		}

		if ( ! function_exists( 'openssl_decrypt' ) ) {
			// Fallback: try base64 decode.
			// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode -- Benign: decryption fallback for API credentials.
			return base64_decode( $encrypted_data, true );
		}

		$key = $this->get_encryption_key();
		// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode -- Benign: decryption of stored API credentials.
		$data = base64_decode( $encrypted_data, true );
		if ( false === $data ) {
			return false;
		}

		$iv_length = openssl_cipher_iv_length( 'AES-256-CBC' );
		$iv        = substr( $data, 0, $iv_length );
		$encrypted = substr( $data, $iv_length );
		return openssl_decrypt( $encrypted, 'AES-256-CBC', $key, 0, $iv );
	}

	/**
	 * Get encryption key (uses WordPress salt)
	 *
	 * @return string Encryption key.
	 */
	private function get_encryption_key() {
		$key = get_option( 'voicelogica_ai_encryption_key' );
		if ( empty( $key ) ) {
			// Generate a key based on WordPress salts.
			$salt        = defined( 'AUTH_SALT' ) ? AUTH_SALT : '';
			$auth_key    = defined( 'AUTH_KEY' ) ? AUTH_KEY : '';
			$admin_email = get_option( 'admin_email', '' );
			$key         = hash( 'sha256', $salt . $auth_key . $admin_email );
			update_option( 'voicelogica_ai_encryption_key', $key );
		}
		return substr( $key, 0, 32 ); // AES-256 requires 32-byte key.
	}

	/**
	 * Send webhook to VoiceLogica with WooCommerce-style signature
	 *
	 * @param string $url    Webhook URL.
	 * @param array  $body   Payload to send.
	 * @param string $topic  Webhook topic (e.g. store.connected, settings.updated).
	 * @param string $secret Webhook secret for HMAC signature. Empty for initial connection.
	 * @return array|WP_Error Response array or error.
	 */
	public function send_webhook( $url, $body, $topic, $secret = '' ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed -- Reserved for future HMAC signing.
		if ( empty( $url ) ) {
			$url = defined( 'VOICELOGICA_AI_WC_WEBHOOK_URL' ) ? VOICELOGICA_AI_WC_WEBHOOK_URL : '';
		}
		if ( empty( $url ) ) {
			return new WP_Error( 'missing_url', __( 'Webhook URL is required.', 'voicelogica-ai-for-woocommerce' ) );
		}

		// Validate URL format.
		$url = esc_url_raw( $url );
		if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) {
			return new WP_Error( 'invalid_url', __( 'Invalid webhook URL format.', 'voicelogica-ai-for-woocommerce' ) );
		}

		// Require HTTPS for security (allow localhost for development).
		$parsed_url   = wp_parse_url( $url );
		$is_localhost = in_array( $parsed_url['host'] ?? '', array( 'localhost', '127.0.0.1', '::1' ), true );
		if ( 'https' !== ( $parsed_url['scheme'] ?? '' ) && ! $is_localhost ) {
			return new WP_Error( 'insecure_url', __( 'Webhook URL must use HTTPS for security.', 'voicelogica-ai-for-woocommerce' ) );
		}

		$raw_body = wp_json_encode( $body );
		if ( false === $raw_body ) {
			return new WP_Error(
				'json_encode_failed',
				'Failed to encode webhook payload: ' . json_last_error_msg()
			);
		}

		$headers = array(
			'Content-Type'               => 'application/json',
			'User-Agent'                 => 'VoiceLogica-WooCommerce/' . VOICELOGICA_AI_WC_VERSION,
			'ngrok-skip-browser-warning' => 'true',
		);

		$response = wp_remote_post(
			$url,
			array(
				'timeout'     => 300,
				'redirection' => 0,
				'headers'     => $headers,
				'body'        => $raw_body,
				'sslverify'   => true,
			)
		);

		if ( is_wp_error( $response ) ) {
			return $response;
		}

		$code = wp_remote_retrieve_response_code( $response );
		$body = wp_remote_retrieve_body( $response );

		if ( $code < 200 || $code >= 300 ) {
			$code_message  = wp_remote_retrieve_response_message( $response );
			$error_message = sprintf(
				/* translators: 1: HTTP status code (number), 2: HTTP status message (text) */
				__( 'HTTP %1$d - %2$s', 'voicelogica-ai-for-woocommerce' ),
				$code,
				$code_message ? $code_message : 'Error'
			);
			$error_message .= "\n\n" . __( 'URL requested:', 'voicelogica-ai-for-woocommerce' ) . ' ' . $url;

			if ( ! empty( $body ) ) {
				$error_message .= "\n\n" . __( 'Raw response:', 'voicelogica-ai-for-woocommerce' ) . "\n";
				$error_message .= wp_strip_all_tags( $body );
			}

			return new WP_Error( 'webhook_failed', $error_message, array( 'status' => $code ) );
		}

		return array(
			'code' => $code,
			'body' => $body,
		);
	}

	/**
	 * Send webhook with API keys in custom headers (secure - keys not in body).
	 *
	 * @param string $url           Webhook URL.
	 * @param array  $body          Payload to send.
	 * @param string $topic         Webhook topic.
	 * @param string $consumer_key  Consumer key for headers.
	 * @param string $consumer_secret Consumer secret for headers.
	 * @return array|WP_Error Response array or error.
	 */
	private function send_webhook_with_auth( $url, $body, $topic, $consumer_key, $consumer_secret ) {
		// Validate URL format.
		$url = esc_url_raw( $url );
		if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) {
			return new WP_Error( 'invalid_url', __( 'Invalid webhook URL format.', 'voicelogica-ai-for-woocommerce' ) );
		}

		// Require HTTPS for security (allow localhost for development).
		$parsed_url   = wp_parse_url( $url );
		$is_localhost = in_array( $parsed_url['host'] ?? '', array( 'localhost', '127.0.0.1', '::1' ), true );
		if ( 'https' !== ( $parsed_url['scheme'] ?? '' ) && ! $is_localhost ) {
			return new WP_Error( 'insecure_url', __( 'Webhook URL must use HTTPS for security.', 'voicelogica-ai-for-woocommerce' ) );
		}

		$raw_body = wp_json_encode( $body );
		if ( false === $raw_body ) {
			return new WP_Error(
				'json_encode_failed',
				'Failed to encode webhook payload: ' . json_last_error_msg()
			);
		}

		// Send API keys in custom headers (secure - not in request body, protected by HTTPS).
		$headers = array(
			'Content-Type'               => 'application/json',
			'User-Agent'                 => 'VoiceLogica-WooCommerce/' . VOICELOGICA_AI_WC_VERSION,
			'X-WC-Consumer-Key'          => $consumer_key,
			'X-WC-Consumer-Secret'       => $consumer_secret,
			'ngrok-skip-browser-warning' => 'true',
		);

		$response = wp_remote_post(
			$url,
			array(
				'timeout'     => 300,
				'redirection' => 0,
				'headers'     => $headers,
				'body'        => $raw_body,
				'sslverify'   => true,
			)
		);

		if ( is_wp_error( $response ) ) {
			return $response;
		}

		$code = wp_remote_retrieve_response_code( $response );
		$body = wp_remote_retrieve_body( $response );

		if ( $code < 200 || $code >= 300 ) {
			$code_message  = wp_remote_retrieve_response_message( $response );
			$error_message = sprintf(
				/* translators: 1: HTTP status code (number), 2: HTTP status message (text) */
				__( 'HTTP %1$d - %2$s', 'voicelogica-ai-for-woocommerce' ),
				$code,
				$code_message ? $code_message : 'Error'
			);
			$error_message .= "\n\n" . __( 'URL requested:', 'voicelogica-ai-for-woocommerce' ) . ' ' . $url;

			if ( ! empty( $body ) ) {
				$error_message .= "\n\n" . __( 'Raw response:', 'voicelogica-ai-for-woocommerce' ) . "\n";
				$error_message .= wp_strip_all_tags( $body );
			}

			return new WP_Error( 'webhook_failed', $error_message, array( 'status' => $code ) );
		}

		return array(
			'code' => $code,
			'body' => $body,
		);
	}

	/**
	 * Generate and store webhook secret.
	 *
	 * @return string
	 */
	public function generate_webhook_secret() {
		$secret = 'vl_wc_' . bin2hex( random_bytes( 24 ) );
		update_option( self::OPTION_WEBHOOK_SECRET, $secret );
		return $secret;
	}

	/**
	 * Get stored webhook secret
	 *
	 * @return string|null
	 */
	public function get_webhook_secret() {
		return get_option( self::OPTION_WEBHOOK_SECRET, null );
	}

	/**
	 * Get stored API consumer key (decrypted)
	 *
	 * @return string|false Consumer key or false if not found.
	 */
	public function get_api_consumer_key() {
		$encrypted = get_option( self::OPTION_API_CONSUMER_KEY );
		if ( empty( $encrypted ) ) {
			return false;
		}
		return $this->decrypt_data( $encrypted );
	}

	/**
	 * Get stored API consumer secret (decrypted)
	 *
	 * @return string|false Consumer secret or false if not found.
	 */
	public function get_api_consumer_secret() {
		$encrypted = get_option( self::OPTION_API_CONSUMER_SECRET );
		if ( empty( $encrypted ) ) {
			return false;
		}
		return $this->decrypt_data( $encrypted );
	}

	/**
	 * Delete WooCommerce API key.
	 *
	 * Uses direct table access because WooCommerce does not provide a public PHP API
	 * for deleting REST API keys; key management is admin-UI only (WC_Admin_API_Keys).
	 * All values are from trusted options and use $wpdb placeholders.
	 *
	 * @return bool True on success, false on failure.
	 */
	public function delete_woocommerce_api_key() {
		$api_key_id = absint( get_option( self::OPTION_API_KEY_ID, 0 ) );
		if ( $api_key_id <= 0 ) {
			return false;
		}

		global $wpdb;
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- WooCommerce has no public API for deleting REST API keys.
		$deleted = $wpdb->delete(
			$wpdb->prefix . 'woocommerce_api_keys',
			array( 'key_id' => $api_key_id ),
			array( '%d' )
		);

		// Clear stored credentials.
		delete_option( self::OPTION_API_KEY_ID );
		delete_option( self::OPTION_API_CONSUMER_KEY );
		delete_option( self::OPTION_API_CONSUMER_SECRET );

		return false !== $deleted;
	}

	/**
	 * Create WooCommerce REST API key.
	 *
	 * Uses direct table access because WooCommerce does not provide a public PHP API
	 * for creating REST API keys; key generation is admin-UI only. Column structure
	 * matches WooCommerce. Table existence is checked before insert.
	 *
	 * @return array|WP_Error Array with consumer_key and consumer_secret, or error.
	 */
	public function create_woocommerce_api_key() {
		if ( ! class_exists( 'WooCommerce' ) ) {
			return new WP_Error( 'woocommerce_not_active', __( 'WooCommerce is not active.', 'voicelogica-ai-for-woocommerce' ) );
		}

		// Get admin user.
		$admin_email = get_option( 'admin_email' );
		$admin_user  = $admin_email ? get_user_by( 'email', $admin_email ) : null;
		if ( ! $admin_user ) {
			return new WP_Error( 'no_admin_user', __( 'Admin user not found.', 'voicelogica-ai-for-woocommerce' ) );
		}

		// Delete existing API key if one exists.
		$existing_key_id = absint( get_option( self::OPTION_API_KEY_ID, 0 ) );
		if ( $existing_key_id > 0 ) {
			global $wpdb;
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- WooCommerce has no public API for API key management.
			$wpdb->delete(
				$wpdb->prefix . 'woocommerce_api_keys',
				array( 'key_id' => $existing_key_id ),
				array( '%d' )
			);
			// Clear stored credentials..
			delete_option( self::OPTION_API_CONSUMER_KEY );
			delete_option( self::OPTION_API_CONSUMER_SECRET );
		}

		// Generate consumer key and secret.
		// Use WooCommerce function if available, otherwise use WordPress functions.
		if ( function_exists( 'wc_rand_hash' ) ) {
			$consumer_key    = 'ck_' . wc_rand_hash();
			$consumer_secret = 'cs_' . wc_rand_hash();
		} else {
			$consumer_key    = 'ck_' . bin2hex( random_bytes( 32 ) );
			$consumer_secret = 'cs_' . bin2hex( random_bytes( 32 ) );
		}

		// Insert API key into database.
		global $wpdb;
		$description = sanitize_text_field( 'Voice Logica AI Integration' );
		$permissions = 'read_write'; // Full access for VoiceLogica.

		// Hash the consumer key for storage (WooCommerce stores hashed keys).
		$hashed_key = function_exists( 'wc_api_hash' ) ? wc_api_hash( $consumer_key ) : hash_hmac( 'sha256', $consumer_key, 'wc-api' );

		// Validate user ID.
		$user_id = absint( $admin_user->ID );
		if ( $user_id <= 0 ) {
			return new WP_Error( 'invalid_user', __( 'Invalid user ID.', 'voicelogica-ai-for-woocommerce' ) );
		}

		// Check if table exists (WooCommerce should create it, but check to be safe).
		$table_name = $wpdb->prefix . 'woocommerce_api_keys';
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Table existence check; no WP API for WooCommerce API keys table.
		$table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) ) === $table_name;

		if ( ! $table_exists ) {
			return new WP_Error( 'table_missing', __( 'WooCommerce API keys table not found. Please ensure WooCommerce is properly installed.', 'voicelogica-ai-for-woocommerce' ) );
		}

		// Insert API key - WooCommerce will display this in Settings > Advanced > REST API.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- WooCommerce has no public API for creating REST API keys.
		$inserted = $wpdb->insert(
			$table_name,
			array(
				'user_id'         => $user_id,
				'description'     => $description,
				'permissions'     => $permissions,
				'consumer_key'    => $hashed_key,
				'consumer_secret' => $consumer_secret,
				'truncated_key'   => substr( $consumer_key, -7 ),
				'last_access'     => null,
			),
			array( '%d', '%s', '%s', '%s', '%s', '%s', '%s' )
		);

		if ( false === $inserted || $wpdb->last_error ) {
			// Don't expose database errors to users.
			return new WP_Error( 'api_key_creation_failed', __( 'Failed to create API key. Please try again.', 'voicelogica-ai-for-woocommerce' ) );
		}

		$key_id = $wpdb->insert_id;

		// Verify the key was inserted correctly.
		if ( empty( $key_id ) ) {
			return new WP_Error( 'api_key_creation_failed', __( 'API key was not created. Please try again.', 'voicelogica-ai-for-woocommerce' ) );
		}

		// Store key ID and encrypted credentials for future reference.
		update_option( self::OPTION_API_KEY_ID, $key_id );
		update_option( self::OPTION_API_CONSUMER_KEY, $this->encrypt_data( $consumer_key ), false ); // autoload = false for security.
		update_option( self::OPTION_API_CONSUMER_SECRET, $this->encrypt_data( $consumer_secret ), false ); // autoload = false for security.

		return array(
			'key_id'          => $key_id,
			'consumer_key'    => $consumer_key,
			'consumer_secret' => $consumer_secret,
		);
	}

	/**
	 * Connect store to VoiceLogica
	 *
	 * @param string $connect_url VoiceLogica API URL.
	 * @return array|WP_Error Response on success, or error.
	 */
	public function connect_store( $connect_url ) {
		// Validate and sanitize URL.
		$connect_url = esc_url_raw( $connect_url );
		if ( empty( $connect_url ) || ! filter_var( $connect_url, FILTER_VALIDATE_URL ) ) {
			return new WP_Error( 'invalid_url', __( 'Invalid connection URL.', 'voicelogica-ai-for-woocommerce' ) );
		}

		// Check user capabilities.
		if ( ! current_user_can( 'manage_woocommerce' ) ) {
			return new WP_Error( 'insufficient_permissions', __( 'You do not have permission to connect the store.', 'voicelogica-ai-for-woocommerce' ) );
		}

		$this->generate_webhook_secret();

		// Create WooCommerce REST API key.
		$api_key_result = $this->create_woocommerce_api_key();
		if ( is_wp_error( $api_key_result ) ) {
			return $api_key_result;
		}

		$admin_email = get_option( 'admin_email' );
		$admin_user  = $admin_email ? get_user_by( 'email', $admin_email ) : null;
		$admin_name  = $admin_user ? $admin_user->display_name : '';

		$country_code = get_option( 'woocommerce_default_country', '' );
		if ( ! empty( $country_code ) && false !== strpos( $country_code, ':' ) ) {
			$country_code = substr( $country_code, 0, strpos( $country_code, ':' ) );
		}
		$country_name = '';
		if ( function_exists( 'WC' ) && WC()->countries && ! empty( $country_code ) ) {
			$countries    = WC()->countries->get_countries();
			$country_name = isset( $countries[ $country_code ] ) ? $countries[ $country_code ] : $country_code;
		} else {
			$country_name = $country_code;
		}

		$store_address     = array_filter(
			array(
				get_option( 'woocommerce_store_address', '' ),
				get_option( 'woocommerce_store_address_2', '' ),
				get_option( 'woocommerce_store_city', '' ),
				get_option( 'woocommerce_store_postcode', '' ),
				$country_name,
			)
		);
		$store_address_str = implode( ', ', $store_address );

		// Get site ID (blog ID in WordPress - works for both single-site and multisite).
		$site_id = function_exists( 'get_current_blog_id' ) ? get_current_blog_id() : 1;

		$payload = array(
			'topic'       => 'store.connected',
			'site_id'     => $site_id,
			'url'         => home_url(),
			'name'        => get_bloginfo( 'name' ),
			'country'     => $country_name,
			'address'     => $store_address_str,
			'admin_email' => $admin_email,
			'admin_name'  => $admin_name,
		);

		// Send with HTTP Basic Auth headers (keys sent securely in headers, not in body).
		$result = $this->send_webhook_with_auth( $connect_url, $payload, 'store.connected', $api_key_result['consumer_key'], $api_key_result['consumer_secret'] );
		if ( is_wp_error( $result ) ) {
			return $result;
		}

		$body = json_decode( $result['body'], true );
		return ( $body ? $body : array( 'success' => true ) );
	}

	/**
	 * Send feature settings update to VoiceLogica
	 *
	 * @param string $webhook_url Webhook URL.
	 * @param array  $features    Feature flags.
	 * @return array|WP_Error
	 */
	public function send_features_updated( $webhook_url, $features ) {
		$secret = $this->get_webhook_secret();
		if ( empty( $secret ) ) {
			return new WP_Error( 'missing_secret', __( 'Webhook secret not found. Please reconnect.', 'voicelogica-ai-for-woocommerce' ) );
		}

		// Use snake_case for payload keys (consistent with other payload keys).
		$api_features = array();

		// Map feature flags.
		$api_features['get_customer_orders'] = ! empty( $features['get_customer_orders'] );

		$api_features['get_product_by_name'] = ! empty( $features['get_product_by_name'] );
		$api_features['get_product_by_code'] = ! empty( $features['get_product_by_code'] );
		$api_features['create_order']        = ! empty( $features['create_order'] );
		$api_features['get_order_by_id']     = ! empty( $features['get_order_by_id'] );

		// Include product_code_type only if get_product_by_code is true.
		if ( $api_features['get_product_by_code'] && ! empty( $features['product_code_type'] ) ) {
			$api_features['product_code_type'] = $features['product_code_type'];
		}

		// Include notify_emails_on_order_creation only if create_order is true.
		if ( $api_features['create_order'] && ! empty( $features['notify_emails_on_order_creation'] ) && is_array( $features['notify_emails_on_order_creation'] ) ) {
			$api_features['notify_emails_on_order_creation'] = $features['notify_emails_on_order_creation'];
		}

		$admin_email = get_option( 'admin_email', '' );
		// Get site ID (blog ID in WordPress - works for both single-site and multisite).
		$site_id = function_exists( 'get_current_blog_id' ) ? get_current_blog_id() : 1;

		$payload = array(
			'topic'       => 'settings.updated',
			'site_id'     => $site_id,
			'url'         => home_url(),
			'admin_email' => $admin_email,
			'features'    => $api_features,
		);

		return $this->send_webhook( $webhook_url, $payload, 'settings.updated', $secret );
	}
}
