<?php
/**
 * Authorize.net Gateway Refund Void Class
 *
 * Handles Refund Void payments through Authorize.net gateway for WooCommerce.
 *
 * @package    Authorize_Net_Payments_For_Woocommerce
 * @subpackage Authorize_Net_Payments_For_Woocommerce/admin
 * @since      1.0.0
 */

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

use net\authorize\api\contract\v1 as AnetAPI;
use net\authorize\api\controller as AnetController;

require_once plugin_dir_path(__FILE__) . '../../trait-anet-order-meta.php';
/**
 * Anet_Refund_Void_Api class.
 *
 * Communicates with Authorize.net API.
 */
class Anet_Refund_Void_Api
{
	use Anet_Order_Meta;
	/**
	 * Checks whether order is fully refunded or not.
	 *
	 * @param object $order oder object.
	 *
	 * @return bool
	 */
	public function is_order_fully_refunded($order)
	{
		if (is_numeric($order)) {
			$order = wc_get_order($order);
		}
		if (!$order instanceof WC_Order) {
			return false;
		}
		$total     = floatval($order->get_total());
		$refunded  = floatval($order->get_total_refunded());
		$order_refunded = $total > 0 && $total === $refunded;
		return $order_refunded;
	}
	/**
	 * Sends a void transaction request to the payment gateway for a given order.
	 *
	 * @param string $transactionid The transaction ID to be voided.
	 * @param int    $order_id      The WooCommerce order ID.
	 *
	 */
	public static function void($transactionid, $order_id)
	{
		try {
			$order = wc_get_order($order_id);
			if (!$order) {
				return new WP_Error('invalid_order', __('Invalid Order ID.', 'authorize-net-payments-for-woocommerce'));
			}
			if (!$transactionid) {
				return new WP_Error('transaction_id_required', __('Transaction ID is required.', 'authorize-net-payments-for-woocommerce'));
			}
			// Store original status to prevent WooCommerce from auto-changing to "refunded".
			$original_status = $order->get_status();
			// Hook to prevent WooCommerce from changing status to "refunded" for void transactions.
			add_filter('woocommerce_order_fully_refunded_status', function ($status, $order_id_filter) use ($order_id, $original_status) {
				if ($order_id_filter === $order_id && ANET_WOOCOMMERCE_ORDER_STATUS_ON_HOLD === $original_status) {
					// Keep the original status, don't change to refunded.
					return $original_status;
				}
				return $status;
			}, ANET_ACTION_HOOK_DEFAULT_PRIORITY, ANET_VAL_TWO);
			$order->get_status();
			// Set up merchant authentication.
			$helper = new Anet_Api_Helper(ANET_PAYMENT_TYPE_CREDIT_CARD);
			$merchant_auth_request = $helper->merchant_authentication_request();
			// Create the transaction request.
			$transaction_request = new AnetAPI\TransactionRequestType();
			$transaction_request->setTransactionType(ANET_TRANSACTION_TYPE_VOID);
			$transaction_request->setRefTransId($transactionid);
			$transaction_request->setSolution($helper->get_solution_id());
			// Create the transaction request.
			$request = new AnetAPI\CreateTransactionRequest();
			$request->setMerchantAuthentication($merchant_auth_request);
			$request->setTransactionRequest($transaction_request);
			$sanitized_request = Anet_Api_Helper::mask_sensitive_data($request); // Call the mask_sensitive_data function.
			Anet_Setting_Helper::log("\nRequest:\n" . $sanitized_request, ANET_PAYMENT_TYPE_CREDIT_CARD, 'debug');
			// Create the transaction controller and execute the request.
			$controller = new AnetController\CreateTransactionController($request);
			$response = $controller->executeWithApiResponse($helper->controller_response());
			Anet_Setting_Helper::log("\nResponse:\n" . wp_json_encode($response->getTransactionResponse(), JSON_PRETTY_PRINT) . wp_json_encode($response, JSON_PRETTY_PRINT), ANET_PAYMENT_TYPE_CREDIT_CARD, 'debug');
			if (ANET_SANDBOX_STATUS_AUTHORIZED === $response->getMessages()->getResultCode() && $response) {
				$transaction_response = $response->getTransactionResponse();
				$result['trans_id'] = $transaction_response->getTransId();
				$self = new self();
				$self->update_order_meta($order, '_anet_credit_card_void_id', isset($result['trans_id']) ? $result['trans_id'] : '');
				if ($transaction_response && $transaction_response->getMessages()) {
					if (ANET_WOOCOMMERCE_ORDER_STATUS_ON_HOLD === $order->get_status()) {
						$order->update_status(ANET_WOOCOMMERCE_ORDER_STATUS_CANCELLED, '');
					}
					// Add order note for void transaction ID.
					/* translators: %s: Void Transaction ID returned from the payment gateway */
					$void_message = sprintf(__("Void Transaction ID: %s", 'authorize-net-payments-for-woocommerce'), $transaction_response->getTransId());
					$order->add_order_note($void_message);
					$order->save();
					return true;
				} else {
					return new WP_Error('void_failed', __('Void transaction failed with no messages.', 'authorize-net-payments-for-woocommerce'));
				}
			}
		} catch (Throwable $e) {
			$error_message = 'Unable to process your request. Please try again later.';
			if ($response && $response->getTransactionResponse() && $response->getTransactionResponse()->getErrors()) {
				$error_message = $response->getTransactionResponse()->getErrors()[0]->getErrorText();
			} elseif ($response && $response->getMessages()) {
				$error_message = $response->getMessages()->getMessage()[0]->getText();
			} else {
				// Fallback to exception message.
				$error_message = $e->getMessage();
			}
			return new WP_Error('void_failed', $error_message);
		}
	}
	/**
	 * Processes a refund transaction through the payment gateway.
	 *
	 * @param string      $transactionid   The original transaction ID to refund.
	 * @param int         $order_id        The WooCommerce order ID.
	 * @param float|null  $refund_amount   Optional refund amount. If null, calculates the remaining refundable amount.
	 *
	 */
	public static function refund($transactionid, $order_id, $refund_amount = null)
	{
		try {
			$order = wc_get_order($order_id);
			if (!$order) {
				return new WP_Error('invalid_order', __('Invalid Order ID.', 'authorize-net-payments-for-woocommerce'));
			}
			if (!$transactionid) {
				return new WP_Error('transaction_id_required', __('Transaction ID is required.', 'authorize-net-payments-for-woocommerce'));
			}
			// Merchant Authentication.
			$helper = new Anet_Api_Helper(ANET_PAYMENT_TYPE_CREDIT_CARD);
			$merchant_auth_request = $helper->merchant_authentication_request();
			// Refund amount.
			$amount = $refund_amount ? floatval($refund_amount) : $order->get_total() - $order->get_total_refunded();
			// Fetch card details from original transaction.
			$card_number = '';
			$exp_date = '';
			$details_request = new AnetAPI\GetTransactionDetailsRequest();
			$details_request->setMerchantAuthentication($merchant_auth_request);
			$details_request->setTransId($transactionid);
			$details_controller = new AnetController\GetTransactionDetailsController($details_request);
			$details_response = $details_controller->executeWithApiResponse($helper->controller_response());
			if (ANET_SANDBOX_STATUS_AUTHORIZED === $details_response->getMessages()->getResultCode() && $details_response) {
				$transaction = $details_response->getTransaction();
				if ($transaction && $transaction->getPayment()) {
					$card = $transaction->getPayment()->getCreditCard();
					if ($card) {
						$full_card_number = $card->getCardNumber();
						$card_number = substr($full_card_number, -4); // Only last 4 digits.
						$exp_date = $card->getExpirationDate();
					}
				}
			} else {
				Anet_Setting_Helper::log("\nFailed to fetch card details:\n" . $details_response->getMessages()->getMessage()[0]->getText(), ANET_PAYMENT_TYPE_CREDIT_CARD, 'error');
			}
		} catch (Exception $e) {
			Anet_Setting_Helper::log("\nException while fetching card details:\n" . $e->getMessage(), ANET_PAYMENT_TYPE_CREDIT_CARD, 'error');
		}
		if (empty($card_number) || empty($exp_date)) {
			return new WP_Error('refund_failed', __('Unable to retrieve card details for refund.', 'authorize-net-payments-for-woocommerce'));
		}
		try {
			// Payment.
			$credit_card = new AnetAPI\CreditCardType();
			$credit_card->setCardNumber($card_number);
			$credit_card->setExpirationDate($exp_date);
			$payment_type = new AnetAPI\PaymentType();
			$payment_type->setCreditCard($credit_card);
			// Transaction Request.
			$transaction_request = new AnetAPI\TransactionRequestType();
			$transaction_request->setTransactionType(ANET_TRANSACTION_TYPE_REFUND);
			$transaction_request->setAmount($amount);
			$transaction_request->setPayment($payment_type);
			$transaction_request->setRefTransId($transactionid);
			$transaction_request->setSolution($helper->get_solution_id());
			// Create Transaction Request.
			$request = new AnetAPI\CreateTransactionRequest();
			$request->setMerchantAuthentication($merchant_auth_request);
			$request->setTransactionRequest($transaction_request);
			if (isset($order_id)) {
				$request->setRefId($order_id);
			}
			$sanitized_request = Anet_Api_Helper::mask_sensitive_data($request); // Call the mask_sensitive_data function.
			Anet_Setting_Helper::log("\nRequest:\n" . $sanitized_request, ANET_PAYMENT_TYPE_CREDIT_CARD, 'debug');
			// Create the transaction controller and execute the request.
			$controller = new AnetController\CreateTransactionController($request);
			$response = $controller->executeWithApiResponse($helper->controller_response());
			Anet_Setting_Helper::log("\nResponse:\n" . wp_json_encode($response->getTransactionResponse(), JSON_PRETTY_PRINT) . wp_json_encode($response, JSON_PRETTY_PRINT), ANET_PAYMENT_TYPE_CREDIT_CARD, 'debug');
			$result = [];
			if (ANET_SANDBOX_STATUS_AUTHORIZED === $response->getMessages()->getResultCode() && $response) {
				$transaction_response = $response->getTransactionResponse();
				if ($transaction_response && $transaction_response->getMessages()) {
					$result['status'] = 'success';
					$result['response_code'] = $transaction_response->getResponseCode();
					$result['trans_id'] = $transaction_response->getTransId();
					$result['code'] = $transaction_response->getMessages()[0]->getCode();
					$result['description'] = $transaction_response->getMessages()[0]->getDescription();
					$self = new self();
					$self->update_order_meta($order, '_anet_credit_card_refund_id', isset($result['trans_id']) ? $result['trans_id'] : '');
					// Add order note for refund transaction id.
					/* translators: %s: Refund Transaction ID returned from the payment gateway */
					$refund_message = sprintf(__("Refund Transaction ID: %s", 'authorize-net-payments-for-woocommerce'), $result['trans_id']);
					$order->add_order_note($refund_message);
					$order->save();
					return true;
				} else {
					return new WP_Error('refund_failed', __('Transaction failed with no messages.', 'authorize-net-payments-for-woocommerce'));
				}
			}
		} catch (Throwable $e) {
			$error_message = 'Unable to process your request. Please try again later.';
			if ($response && $response->getTransactionResponse() && $response->getTransactionResponse()->getErrors()) {
				$error_message = $response->getTransactionResponse()->getErrors()[0]->getErrorText();
			} elseif ($response && $response->getMessages()) {
				$error_message = $response->getMessages()->getMessage()[0]->getText();
			} else {
				// Fallback to exception message.
				$error_message = $e->getMessage();
			}
			Anet_Setting_Helper::log('Refund failed: ' . $error_message, ANET_PAYMENT_TYPE_CREDIT_CARD, 'error');
			return new WP_Error('refund_failed', $error_message);
		}
	}
	/**
	 * Processes an eCheck refund transaction through the payment gateway.
	 *
	 * @param string      $capture_transaction_id  The original transaction ID to refund.
	 * @param int         $order_id                The WooCommerce order ID.
	 * @param float|null  $refund_amount           Optional refund amount. If null, calculates the remaining refundable amount.
	 *
	 */
	public static function refund_echeck($capture_transaction_id, $order_id, $refund_amount = null)
	{
		$order = wc_get_order($order_id);
		if (!$order) {
			return new WP_Error('invalid_order', __('Invalid Order ID.', 'authorize-net-payments-for-woocommerce'));
		}
		if (!$capture_transaction_id) {
			return new WP_Error('transaction_id_required', __('Transaction ID is required.', 'authorize-net-payments-for-woocommerce'));
		}
		// Merchant Authentication.
		$helper = new Anet_Api_Helper(ANET_PAYMENT_TYPE_ECHECK);
		$merchant_auth_request = $helper->merchant_authentication_request();
		// Refund amount.
		$amount = $refund_amount ? floatval($refund_amount) : $order->get_total() - $order->get_total_refunded();
		// Fetch echeck details from original transaction.
		$account_type = '';
		$account_number = '';
		$routing_number = '';
		$name_on_account = '';
		try {
			$details_request = new AnetAPI\GetTransactionDetailsRequest();
			$details_request->setMerchantAuthentication($merchant_auth_request);
			$details_request->setTransId($capture_transaction_id);
			$details_controller = new AnetController\GetTransactionDetailsController($details_request);
			$details_response = $details_controller->executeWithApiResponse($helper->controller_response());
			if (ANET_SANDBOX_STATUS_AUTHORIZED === $details_response->getMessages()->getResultCode() && $details_response) {
				$transaction = $details_response->getTransaction();
				if ($transaction && $transaction->getPayment() && $transaction->getPayment()->getBankAccount()) {
					$bank_account = $transaction->getPayment()->getBankAccount();
					$account_number = $bank_account->getAccountNumber();
					$routing_number = $bank_account->getRoutingNumber();
					$account_type = $bank_account->getAccountType();
					$name_on_account = $bank_account->getNameOnAccount();
				}
			} else {
				Anet_Setting_Helper::log("\nFailed to fetch card details:\n" . $details_response->getMessages()->getMessage()[0]->getText(), ANET_PAYMENT_TYPE_ECHECK, 'error');
			}
		} catch (Exception $e) {
			Anet_Setting_Helper::log("\nException while fetching card details:\n" . $e->getMessage(), ANET_PAYMENT_TYPE_ECHECK, 'error');
		}
		try {
			// Refund eCheck Payment Request.
			$echeck = new AnetAPI\BankAccountType();
			$echeck->setAccountNumber($account_number);
			$echeck->setRoutingNumber($routing_number);
			$echeck->setAccountType($account_type);
			$echeck->setNameOnAccount($name_on_account);
			$payment_type = new AnetAPI\PaymentType();
			$payment_type->setBankAccount($echeck);
			// Transaction Request.
			$transaction_request = new AnetAPI\TransactionRequestType();
			$transaction_request->setTransactionType(ANET_TRANSACTION_TYPE_REFUND);
			$transaction_request->setAmount($amount);
			$transaction_request->setPayment($payment_type);
			$transaction_request->setRefTransId($capture_transaction_id);
			$transaction_request->setSolution($helper->get_solution_id());
			// Create Transaction Request.
			$request = new AnetAPI\CreateTransactionRequest();
			$request->setMerchantAuthentication($merchant_auth_request);
			$request->setTransactionRequest($transaction_request);
			if (isset($order_id)) {
				$request->setRefId($order_id);
			}
			$sanitized_request = Anet_Api_Helper::mask_sensitive_data($request); // Call the mask_sensitive_data function.
			Anet_Setting_Helper::log("\nRequest:\n" . $sanitized_request, ANET_PAYMENT_TYPE_ECHECK, 'debug');
			// Create the transaction controller and execute the request.
			$controller = new AnetController\CreateTransactionController($request);
			$response = $controller->executeWithApiResponse($helper->controller_response());
			Anet_Setting_Helper::log("\nResponse:\n" . wp_json_encode($response->getTransactionResponse(), JSON_PRETTY_PRINT) . wp_json_encode($response, JSON_PRETTY_PRINT), ANET_PAYMENT_TYPE_ECHECK, 'debug');
			$result = [];
			if (ANET_SANDBOX_STATUS_AUTHORIZED === $response->getMessages()->getResultCode() && $response) {
				$transaction_response = $response->getTransactionResponse();
				if ($transaction_response && $transaction_response->getMessages()) {
					$result['status'] = 'success';
					$result['response_code'] = $transaction_response->getResponseCode();
					$result['trans_id'] = $transaction_response->getTransId();
					$result['code'] = $transaction_response->getMessages()[0]->getCode();
					$result['description'] = $transaction_response->getMessages()[0]->getDescription();
					$self = new self();
					$self->update_order_meta($order, '_anet_echeck_refund_id', isset($result['trans_id']) ? $result['trans_id'] : '');
					// Add order note for refund transaction id.
					/* translators: %s: Refund Transaction ID returned from the payment gateway */
					$refund_message = sprintf(__("Refund Transaction ID: %s", 'authorize-net-payments-for-woocommerce'), $result['trans_id']);
					$order->add_order_note($refund_message);
					$order->save();
					return true;
				} else {
					return new WP_Error('refund_failed', __('Transaction failed with no messages.', 'authorize-net-payments-for-woocommerce'));
				}
			}
		} catch (Throwable $e) {
			$error_message = 'Unable to process your request. Please try again later.';
			if ($response && $response->getTransactionResponse() && $response->getTransactionResponse()->getErrors()) {
				$error_message = $response->getTransactionResponse()->getErrors()[0]->getErrorText();
			} elseif ($response && $response->getMessages()) {
				$error_message = $response->getMessages()->getMessage()[0]->getText();
			} else {
				// Fallback to exception message.
				$error_message = $e->getMessage();
			}
			return new WP_Error('refund_failed', $error_message);
		}
	}
}
