<?php
/**
 * Authorize.Net Gateway Reporting API Class
 *
 * Handles Reporting API payments through Authorize.Net gateway for WooCommerce.
 *
 * @package    Authorize_Net_Payments_For_Woocommerce
 * @subpackage Authorize_Net_Payments_For_Woocommerce/includes
 * @since      1.0.0
 */

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

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

/**
 * Handles Fraud Detection Suite (FDS) transaction monitoring and order status updates.
 *
 * This class fetches unsettled transactions from Authorize.Net and updates WooCommerce
 * order statuses based on merchant decisions (approve/decline) in the FDS review process.
 *
 * @since 1.0.0
 */
class Anet_Reporting
{
    /**
     * Fetch unsettled transactions using API call with pagination support.
     */
    public function get_unsettled_transactions()
    {
        try {
            $helper = new Anet_Api_Helper(ANET_PAYMENT_TYPE_CREDIT_CARD);
            $merchant_auth_request = $helper->merchant_authentication_request();
            $offset = 1;
            $limit = 1000;
            $actionable_transactions_found = false;
            do {
                // Create and execute API request.
                $request = new AnetAPI\GetUnsettledTransactionListRequest();
                $request->setMerchantAuthentication($merchant_auth_request);
                // Create pagination object.
                $paging = new AnetAPI\PagingType();
                $paging->setLimit($limit);
                $paging->setOffset($offset);
                $request->setPaging($paging);
                // Create sorting object.
                $sorting = new AnetAPI\TransactionListSortingType();
                $sorting->setOrderBy('submitTimeUTC');
                $sorting->setOrderDescending(true);
                $request->setSorting($sorting);
                $controller = new AnetController\GetUnsettledTransactionListController($request);
                $response = $controller->executeWithApiResponse($helper->controller_response());
                // Stop if no response or no transactions.
                if (!$response || ANET_SANDBOX_STATUS_AUTHORIZED !== $response->getMessages()->getResultCode() || empty($response->getTransactions())) {
                    break;
                }
                // Check for actionable transactions before logging.
                $current_batch_actionable = $this->check_for_actionable_transactions($response->getTransactions());
                if ($current_batch_actionable && !$actionable_transactions_found) {
                    // Log request and response only when actionable transactions are found.
                    $sanitized_request = Anet_Api_Helper::mask_sensitive_data($request);
                    Anet_Setting_Helper::log("Unsettled Transactions Request: " . $sanitized_request, ANET_PAYMENT_TYPE_CREDIT_CARD, 'debug');
                    Anet_Setting_Helper::log("Unsettled Transactions Response: " . wp_json_encode($response, JSON_PRETTY_PRINT) . " | Total Results: " . $response->getTotalNumInResultSet(), ANET_PAYMENT_TYPE_CREDIT_CARD, 'debug');
                    $actionable_transactions_found = true;
                }
                // Process each transaction.
                foreach ($response->getTransactions() as $transaction) {
                    $this->process_single_transaction($transaction);
                }
                // Move to next page.
                $offset += $limit;
            } while ($offset <= $response->getTotalNumInResultSet());
        } catch (\Exception $e) {
            Anet_Setting_Helper::log('Critical error in get_unsettled_transactions: ' . $e->getMessage(), ANET_PAYMENT_TYPE_CREDIT_CARD, 'error');
        }
    }
    /**
     * Check if any transactions in the batch require action (not under FDS review).
     *
     * @param array $transactions Array of transaction objects from API response.
     * @return bool True if any actionable transactions found, false otherwise.
     */
    private function check_for_actionable_transactions($transactions)
    {
        foreach ($transactions as $transaction) {
            $trans_id = $transaction->getTransId();
            $status = $transaction->getTransactionStatus();
            $order = $this->get_order_by_transaction_id($trans_id);
            // Skip if invalid data.
            if (empty($trans_id) || empty($status) || !($order instanceof \WC_Order)) {
                continue;
            }
            // Skip if not Authorize.Net order.
            if ($order->get_payment_method() !== ANET_CREDIT_CARD_GATEWAY_ID) {
                continue;
            }
            // Skip if not FDS order.
            if ($order->get_meta('_anet_credit_card_fds_review') !== ANET_YES) {
                continue;
            }
            // Skip if still under review.
            if (in_array($status, ['FDSPendingReview', 'FDSAuthorizedPendingReview'], true)) {
                continue;
            }
            // Skip if order status is not pending.
            if (!in_array($order->get_status(), [ANET_WOOCOMMERCE_ORDER_STATUS_PENDING_PAYMENT, 'pending'], true)) {
                continue;
            }
            // Found an actionable transaction.
            return true;
        }
        return false;
    }
    /**
     * Process a single transaction from the unsettled transactions list.
     *
     * @param mixed $transaction Transaction object from Authorize.Net API response.
     */
    private function process_single_transaction($transaction)
    {
        try {
            $trans_id = $transaction->getTransId();
            $status = $transaction->getTransactionStatus();
            $order = $this->get_order_by_transaction_id($trans_id);
            // Skip if invalid data.
            if (empty($trans_id) || empty($status) || !($order instanceof \WC_Order)) {
                return;
            }
            // Skip if not Authorize.Net order.
            if ($order->get_payment_method() !== ANET_CREDIT_CARD_GATEWAY_ID) {
                return;
            }
            // Skip if not FDS order.
            if ($order->get_meta('_anet_credit_card_fds_review') !== ANET_YES) {
                return;
            }
            // Skip if still under review.
            if (in_array($status, ['FDSPendingReview', 'FDSAuthorizedPendingReview'], true)) {
                return;
            }
            // Skip if order status is not pending.
            if (!in_array($order->get_status(), [ANET_WOOCOMMERCE_ORDER_STATUS_PENDING_PAYMENT, 'pending'], true)) {
                return;
            }
            // Log and process the transaction.
            Anet_Setting_Helper::log("Processing transaction {$trans_id}: Status={$status}, Order={$order->get_id()}", ANET_PAYMENT_TYPE_CREDIT_CARD, 'debug');
            $this->process_fds_transaction_status($order, $trans_id, $status);
        } catch (\Exception $e) {
            Anet_Setting_Helper::log("Error processing transaction: " . $e->getMessage(), ANET_PAYMENT_TYPE_CREDIT_CARD, 'error');
        }
    }
    /**
     * Process FDS transaction status and update order accordingly.
     *
     * @param \WC_Order $order The WooCommerce order object.
     * @param string $trans_id The transaction ID.
     * @param string $status The transaction status from Authorize.Net.
     * @throws \Exception If invalid order object or missing transaction data.
     */
    private function process_fds_transaction_status($order, $trans_id, $status)
    {
        try {
            // Validate inputs.
            if (!($order instanceof \WC_Order)) {
                throw new \Exception("Invalid order object for transaction {$trans_id}");
            }
            if (empty($trans_id) || empty($status)) {
                throw new \Exception("Missing transaction ID or status for order {$order->get_id()}");
            }
            // Define status handlers map - cleaner than switch case.
            $status_handlers = [
                'authorizedPendingCapture' => [
                    'order_status' => ANET_WOOCOMMERCE_ORDER_STATUS_ON_HOLD,
                    'status_message' => __('Payment transaction authorized after AFDS review', 'authorize-net-payments-for-woocommerce'),
                    'order_note' => __('AFDS Review Completed - Transaction Authorized', 'authorize-net-payments-for-woocommerce'),
                    'log_message' => 'on-hold - transaction authorized'
                ],
                'capturedPendingSettlement' => [
                    'order_status' => ANET_WOOCOMMERCE_ORDER_STATUS_PROCESSING,
                    'status_message' => __('Payment transaction captured after AFDS review', 'authorize-net-payments-for-woocommerce'),
                    'order_note' => __('AFDS Review Completed - Transaction Captured', 'authorize-net-payments-for-woocommerce'),
                    'log_message' => 'processing - transaction captured',
                    'extra_meta' => ['_anet_credit_card_charge_captured' => ANET_YES]
                ],
                'declined' => [
                    'order_status' => ANET_WOOCOMMERCE_ORDER_STATUS_CANCELLED,
                    'status_message' => __('Payment transaction declined after AFDS review', 'authorize-net-payments-for-woocommerce'),
                    'order_note' => __('AFDS Review Completed - Transaction Declined', 'authorize-net-payments-for-woocommerce'),
                    'log_message' => 'cancelled - transaction declined',
                    'extra_meta' => ['_anet_credit_card_fds_decline' => ANET_YES]
                ],
                'voided' => [
                    'order_status' => ANET_WOOCOMMERCE_ORDER_STATUS_CANCELLED,
                    'status_message' => __('Payment transaction voided after AFDS review', 'authorize-net-payments-for-woocommerce'),
                    'order_note' => __('AFDS Review Completed - Transaction Voided', 'authorize-net-payments-for-woocommerce'),
                    'log_message' => 'cancelled - transaction voided'
                ],
                'expired' => [
                    'order_status' => ANET_WOOCOMMERCE_ORDER_STATUS_CANCELLED,
                    'status_message' => __('Payment authorization expired', 'authorize-net-payments-for-woocommerce'),
                    'order_note' => __('AFDS Review - Authorization Expired', 'authorize-net-payments-for-woocommerce'),
                    'log_message' => 'cancelled - authorization expired'
                ],
                'generalError' => [
                    'order_status' => ANET_WOOCOMMERCE_ORDER_STATUS_FAILED,
                    'status_message' => __('Payment processing error', 'authorize-net-payments-for-woocommerce'),
                    'order_note' => __('AFDS Review - General Error Occurred', 'authorize-net-payments-for-woocommerce'),
                    'log_message' => 'failed - general error'
                ]
            ];
            // Handle settled transactions separately (has condition).
            if ('settledSuccessfully' === $status) {
                if (in_array($order->get_status(), [ANET_WOOCOMMERCE_ORDER_STATUS_PROCESSING, ANET_WOOCOMMERCE_ORDER_STATUS_ON_HOLD], true)) {
                    $this->update_order_after_fds(
                        $order,
                        ANET_WOOCOMMERCE_ORDER_STATUS_COMPLETED,
                        __('Payment transaction settled successfully', 'authorize-net-payments-for-woocommerce'),
                        __('Payment Settled Successfully', 'authorize-net-payments-for-woocommerce'),
                        'completed - payment settled'
                    );
                }
                return;
            }
            // Process status using handler map.
            if (isset($status_handlers[$status])) {
                $handler = $status_handlers[$status];
                // Add extra meta data if specified.
                if (isset($handler['extra_meta'])) {
                    foreach ($handler['extra_meta'] as $meta_key => $meta_value) {
                        $order->update_meta_data($meta_key, $meta_value);
                    }
                }
                // Update order using handler configuration.
                $this->update_order_after_fds(
                    $order,
                    $handler['order_status'],
                    $handler['status_message'],
                    $handler['order_note'],
                    $handler['log_message']
                );
            } else {
                // Unknown or unhandled status.
                Anet_Setting_Helper::log("Order {$order->get_id()} - Unhandled transaction status: {$status}", ANET_PAYMENT_TYPE_CREDIT_CARD, 'warning');
            }
        } catch (\Exception $e) {
            Anet_Setting_Helper::log("Error in process_fds_transaction_status for order {$order->get_id()}: " . $e->getMessage(), ANET_PAYMENT_TYPE_CREDIT_CARD, 'error');
            throw $e; // Re-throw to be caught by calling function.
        }
    }
    /**
     * Get WooCommerce order by transaction ID.
     * 
     * @param string $trans_id The transaction ID to search for.
     * @return \WC_Order|null The WooCommerce order object or null if not found.
     */
    private function get_order_by_transaction_id($trans_id)
    {
        return $this->find_order_by_transaction_id($trans_id);
    }
    /**
     * Find order by transaction ID using multiple meta keys.
     * 
     * @param string $trans_id The transaction ID to search for.
     * @return \WC_Order|null The WooCommerce order object or null if not found.
     */
    private function find_order_by_transaction_id($trans_id)
    {
        if (empty($trans_id)) {
            return null;
        }
        $meta_keys = ['_anet_credit_card_authorization_id', '_anet_credit_card_charge_id'];
        foreach ($meta_keys as $meta_key) {
            $orders = wc_get_orders([
                'meta_query' => [ // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Required for FDS transaction lookup by ID, limited to 1 result
                    [
                        'key'   => $meta_key,
                        'value' => $trans_id,
                        'compare' => '='
                    ]
                ],
                'limit' => 1
            ]);
            if (!empty($orders) && ANET_CREDIT_CARD_GATEWAY_ID === $orders[0]->get_payment_method()) {
                return $orders[0];
            }
        }
        return null;
    }
    /**
     * Helper method to update order status after FDS processing.
     *
     * @param \WC_Order $order The WooCommerce order object to update.
     * @param string $new_status The new order status to set.
     * @param string $status_message The status message for the order update.
     * @param string $order_note The note to add to the order.
     * @param string $log_message The message to log for debugging.
     */
    private function update_order_after_fds($order, $new_status, $status_message, $order_note, $log_message)
    {
        $order->update_status($new_status, $status_message);
        $order->add_order_note($order_note);
        $order->save();
        Anet_Setting_Helper::log("Order {$order->get_id()} {$log_message}.", ANET_PAYMENT_TYPE_CREDIT_CARD, 'info');
    }
}
