<?php

namespace WC_Affiliate\API;

defined( 'ABSPATH' ) || exit;

use WC_Affiliate\Models\Affiliate;
use WC_Affiliate\Models\Referral;
use WC_Affiliate\Models\Transaction as TransactionModel;
use WC_Affiliate\Traits\Auth;
use WC_Affiliate\Traits\Rest;
use WC_Affiliate\Traits\Validation;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;

/**
 * Transaction API Controller
 *
 * Handles REST API requests for transaction management including payout processing,
 * status updates, and transaction history retrieval. Provides endpoints for
 * managing financial transactions with proper validation and access control.
 *
 * @since   1.0.0
 * @package WC_Affiliate\API
 */
class Transaction {

	use Auth;
	use Rest;
	use Validation;

	/**
	 * Get transactions list
	 *
	 * @param WP_REST_Request $request REST request object containing optional filter parameters.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function list( WP_REST_Request $request ) {
		$pagination     = $this->get_pagination_params( $request );
		$affiliate_id   = (int) $request->get_param( 'affiliate_id' );
		$status         = sanitize_text_field( $request->get_param( 'status' ) );
		$payment_method = sanitize_text_field( $request->get_param( 'payment_method' ) );
		$start_date     = sanitize_text_field( $request->get_param( 'start_date' ) );
		$end_date       = sanitize_text_field( $request->get_param( 'end_date' ) );
		$txn_id         = sanitize_text_field( $request->get_param( 'txn_id' ) );
		$search         = sanitize_text_field( $request->get_param( 'search' ) );

		$args = array(
			'limit'   => $pagination['limit'],
			'offset'  => $pagination['offset'],
			'orderby' => 'request_at',
			'order'   => 'DESC',
		);

		if ( $affiliate_id ) {
			$args['affiliate_id'] = $affiliate_id;
		}

		if ( $status ) {
			$args['status'] = $status;
		}

		if ( $payment_method ) {
			$args['payment_method'] = $payment_method;
		}

		if ( $start_date ) {
			$args['start_date'] = $start_date;
		}

		if ( $end_date ) {
			$args['end_date'] = $end_date;
		}

		if ( $txn_id ) {
			$args['txn_id'] = $txn_id;
		}

		if ( $search ) {
			$args['search'] = $search;
		}

		$transaction_model  = new TransactionModel();
		$transactions       = $transaction_model->get_transactions( $args );
		$total_transactions = $transaction_model->get_count( $args );
		$data               = array();

		foreach ( $transactions as $transaction ) {
			$data[] = $transaction->to_array();
		}

		/**
		 * Filter transactions list data before response.
		 *
		 * Allows modification of the transactions list data.
		 *
		 * @since 1.0.0
		 *
		 * @param array           $data    Array of transaction data.
		 * @param array           $args    Query arguments.
		 * @param WP_REST_Request $request The original request object.
		 */
		$data = apply_filters( 'wc_affiliate_rest_api_transactions_list_data', $data, $args, $request );

		$response = $this->response_success( $data );

		return $this->add_pagination_headers( $response, $total_transactions, $pagination['page'], $pagination['per_page'] );
	}

	/**
	 * Process payout for all referrals of an affiliate
	 *
	 * @param WP_REST_Request $request REST request object.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function process_affiliate_payout( WP_REST_Request $request ) {
		$affiliate_id = (int) $request->get_param( 'affiliate_id' );

		$affiliate_model = new Affiliate( $affiliate_id );
		if ( ! $affiliate_model->exists() ) {
			return $this->response_error( __( 'Invalid affiliate ID', 'wc-affiliate' ), 400 );
		}

		$payment_method = $affiliate_model->get( 'payment_method' );
		if ( empty( $payment_method ) ) {
			return $this->response_error( __( 'Invalid payment method', 'wc-affiliate' ), 400 );
		}

		// Get total unpaid commission for this affiliate.
		$referral_model = new Referral();
		$total_amount   = $referral_model->get_total_unpaid_commission_for_affiliate( $affiliate_id );

		if ( $total_amount <= 0 ) {
			return $this->response_error( __( 'No eligible commission found for payout', 'wc-affiliate' ), 400 );
		}

		$payout_result = apply_filters(
			'wc_affiliate_process_payout',
			array(
				'success' => true,
				'message' => __( 'Affiliate payout completed successfully!', 'wc-affiliate' ),
			),
			$affiliate_id,
			$total_amount,
			$payment_method
		);

		if ( ! $payout_result['success'] ) {
			return $this->response_error(
				$payout_result['message'],
				$payout_result['code'] ?? 400
			);
		}

		// Remove payout request badge.
		$affiliate_model->remove_payout_request_badge();

		$result = $this->create_transaction_and_update_referrals(
			$affiliate_id,
			$total_amount,
			$payment_method,
			$payout_result
		);

		return $this->response_success(
			array(
				'message'        => $payout_result['message'],
				'transaction_id' => $result['transaction_id'],
				'total_amount'   => $total_amount,
			)
		);
	}

	/**
	 * Mark affiliate payout as paid (without actual payment processing)
	 *
	 * @param WP_REST_Request $request REST request object.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function mark_affiliate_paid( WP_REST_Request $request ) {
		$affiliate_id    = (int) $request->get_param( 'affiliate_id' );
		$txn_id          = sanitize_text_field( $request->get_param( 'txn_id' ) );
		$payment_method  = sanitize_text_field( $request->get_param( 'payment_method' ) );
		$affiliate_model = new Affiliate( $affiliate_id );

		if ( ! $affiliate_model->exists() ) {
			return $this->response_error( __( 'Invalid affiliate ID', 'wc-affiliate' ), 400 );
		}

		// Get total unpaid commission for this affiliate.
		$referral_model = new Referral();
		$total_amount   = $referral_model->get_total_unpaid_commission_for_affiliate( $affiliate_id );

		if ( $total_amount <= 0 ) {
			return $this->response_error( __( 'No eligible commission found for payout', 'wc-affiliate' ), 400 );
		}

		$payout_result = array(
			'txn_id' => ! empty( $txn_id ) ? $txn_id : 'MANUAL-' . time() . '-' . $affiliate_id,
		);

		$result = $this->create_transaction_and_update_referrals(
			$affiliate_id,
			$total_amount,
			$payment_method,
			$payout_result
		);

		do_action( 'wc_affiliate_affiliate_marked_paid', $affiliate_id, $total_amount, $result['transaction_id'] );

		do_action( 'wc_affiliate_payout_paid', $affiliate_id, $total_amount );

		// Remove payout request badge.
		$affiliate_model->remove_payout_request_badge();

		return $this->response_success(
			array(
				'message'        => __( 'Affiliate marked as paid successfully!', 'wc-affiliate' ),
				'transaction_id' => $result['transaction_id'],
				'total_amount'   => $total_amount,
			)
		);
	}

	/**
	 * Create transaction and update referrals
	 *
	 * @param int    $affiliate_id   Affiliate ID.
	 * @param float  $amount         Payout amount.
	 * @param string $payment_method Payment method.
	 * @param array  $payout_result  Payout result from filter.
	 *
	 * @return array Array with transaction_id.
	 */
	public function create_transaction_and_update_referrals( int $affiliate_id, float $amount, string $payment_method, array $payout_result ): array {
		$txn_id            = $payout_result['txn_id'] ?? 'BULK-' . time() . '-' . $affiliate_id;
		$transaction_model = new TransactionModel();
		$transaction       = $transaction_model->create(
			array(
				'affiliate'      => $affiliate_id,
				'amount'         => $amount,
				'payment_method' => $payment_method,
				'txn_id'         => $txn_id,
				'process_at'     => time(),
				'request_at'     => time(),
				'status'         => 'completed',
			)
		);

		$referral_model     = new Referral();
		$approved_referrals = $referral_model->get_approved_referrals_for_affiliate( $affiliate_id );
		$referral_ids       = array_column( $approved_referrals, 'id' );
		$transaction_id     = $transaction->get_id();

		if ( ! empty( $referral_ids ) ) {
			$referral_model->bulk_update_status( $referral_ids, 'paid' );

			// Update transaction_id for each referral.
			foreach ( $approved_referrals as $referral_data ) {
				$referral = new Referral( $referral_data['id'] );
				$referral->update( array( 'transaction_id' => $transaction_id ) );

				do_action( 'wc_affiliate_referral_has_paid', (int) $referral_data['id'], $referral, $transaction );
			}
		}

		return array( 'transaction_id' => $transaction_id );
	}
}
