<?php

namespace WeDevs\DokanPro\Refund;

use Exception;
use WP_Error;
use WC_Order;
use WC_Order_Refund;
use WeDevs\Dokan\Abstracts\DokanModel;

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

class Refund extends DokanModel {

    /**
     * The model data
     *
     * @since 3.0.0
     *
     * @var array
     */
    protected $data = [];

    /**
     * Class constructor
     *
     * @since 3.0.0
     *
     * @param array $data
     */
    public function __construct( $data = [] ) {
        $defaults = [
            'id'              => 0,
            'order_id'        => 0,
            'seller_id'       => 0,
            'refund_amount'   => 0,
            'refund_reason'   => '',
            'item_qtys'       => null,
            'item_totals'     => null,
            'item_tax_totals' => null,
            'restock_items'   => null,
            'date'            => current_time( 'mysql' ),
            'status'          => 0,
            'method'          => '0',
        ];

        $data = wp_parse_args( $data, $defaults );

        $this->set_data( $data );
    }

    /**
     * Set model data
     *
     * @since 3.0.0
     *
     * @param array $data
     *
     * @return void
     */
    protected function set_data( $data ) {
        $data = wp_unslash( $data );

        $this->set_id( $data['id'] )
            ->set_order_id( $data['order_id'] )
            ->set_seller_id( $data['seller_id'] )
            ->set_refund_amount( $data['refund_amount'] )
            ->set_refund_reason( $data['refund_reason'] )
            ->set_item_qtys( $data['item_qtys'] )
            ->set_item_totals( $data['item_totals'] )
            ->set_item_tax_totals( $data['item_tax_totals'] )
            ->set_restock_items( $data['restock_items'] )
            ->set_date( $data['date'] )
            ->set_status( $data['status'] )
            ->set_method( $data['method'] );
    }

    /**
     * Set `id` property
     *
     * @since 3.0.0
     *
     * @param int $id
     *
     * @return Refund
     */
    public function set_id( $id ) {
        $this->data['id'] = $id;
        return $this;
    }

    /**
     * Set `order_id` property
     *
     * @since 3.0.0
     *
     * @param int $order_id
     *
     * @return Refund
     */
    public function set_order_id( $order_id ) {
        $this->data['order_id'] = $order_id;
        return $this;
    }

    /**
     * Set `seller_id` property
     *
     * @since 3.0.0
     *
     * @param int $seller_id
     *
     * @return Refund
     */
    public function set_seller_id( $seller_id ) {
        $this->data['seller_id'] = $seller_id;
        return $this;
    }

    /**
     * Set `refund_amount` property
     *
     * @since 3.0.0
     *
     * @param string $refund_amount
     *
     * @return Refund
     */
    public function set_refund_amount( $refund_amount ) {
        $this->data['refund_amount'] = $refund_amount;
        return $this;
    }

    /**
     * Set `refund_reason` property
     *
     * @since 3.0.0
     *
     * @param string $refund_reason
     *
     * @return Refund
     */
    public function set_refund_reason( $refund_reason ) {
        $this->data['refund_reason'] = $refund_reason;
        return $this;
    }

    /**
     * Set `item_qtys` property
     *
     * @since 3.0.0
     *
     * @param array $item_qtys
     *
     * @return Refund
     */
    public function set_item_qtys( $item_qtys ) {
        $this->data['item_qtys'] = $item_qtys;
        return $this;
    }

    /**
     * Set `item_totals` property
     *
     * @since 3.0.0
     *
     * @param array $item_totals
     *
     * @return Refund
     */
    public function set_item_totals( $item_totals ) {
        $this->data['item_totals'] = $item_totals;
        return $this;
    }

    /**
     * Set `item_tax_totals` property
     *
     * @since 3.0.0
     *
     * @param array $item_tax_totals
     *
     * @return Refund
     */
    public function set_item_tax_totals( $item_tax_totals ) {
        $this->data['item_tax_totals'] = $item_tax_totals;
        return $this;
    }

    /**
     * Set `restock_items` property
     *
     * @since 3.0.0
     *
     * @param array $restock_items
     *
     * @return Refund
     */
    public function set_restock_items( $restock_items ) {
        $this->data['restock_items'] = $restock_items;
        return $this;
    }

    /**
     * Set `date` property
     *
     * @since 3.0.0
     *
     * @param string $date
     *
     * @return Refund
     */
    public function set_date( $date ) {
        $this->data['date'] = $date;
        return $this;
    }

    /**
     * Set `status` property
     *
     * @since 3.0.0
     *
     * @param string $status
     *
     * @return Refund
     */
    public function set_status( $status ) {
        $this->data['status'] = $status;
        return $this;
    }

    /**
     * Set `method` property
     *
     * @since 3.0.0
     *
     * @param string $method
     *
     * @return Refund
     */
    public function set_method( $method ) {
        $this->data['method'] = $method;
        return $this;
    }

    /**
     * Get `id` property
     *
     * @since 3.0.0
     *
     * @return int
     */
    public function get_id() {
        return $this->data['id'];
    }

    /**
     * Get `order_id` property
     *
     * @since 3.0.0
     *
     * @return int
     */
    public function get_order_id() {
        return $this->data['order_id'];
    }

    /**
     * Get `seller_id` property
     *
     * @since 3.0.0
     *
     * @return int
     */
    public function get_seller_id() {
        return $this->data['seller_id'];
    }

    /**
     * Get `refund_amount` property
     *
     * @since 3.0.0
     *
     * @return string
     */
    public function get_refund_amount() {
        return $this->data['refund_amount'];
    }

    /**
     * Get `refund_reason` property
     *
     * @since 3.0.0
     *
     * @return string
     */
    public function get_refund_reason() {
        return $this->data['refund_reason'];
    }

    /**
     * Get `item_qtys` property
     *
     * @since 3.0.0
     *
     * @return array
     */
    public function get_item_qtys() {
        return $this->data['item_qtys'];
    }

    /**
     * Get `item_totals` property
     *
     * @since 3.0.0
     *
     * @return array
     */
    public function get_item_totals() {
        return $this->data['item_totals'];
    }

    /**
     * Get `item_tax_totals` property
     *
     * @since 3.0.0
     *
     * @return array
     */
    public function get_item_tax_totals() {
        return $this->data['item_tax_totals'];
    }

    /**
     * Get `restock_items` property
     *
     * @since 3.0.0
     *
     * @return array
     */
    public function get_restock_items() {
        return $this->data['restock_items'];
    }

    /**
     * Get `date` property
     *
     * @since 3.0.0
     *
     * @return string
     */
    public function get_date() {
        return $this->data['date'];
    }

    /**
     * Get `status` property
     *
     * @since 3.0.0
     *
     * @return string
     */
    public function get_status() {
        return $this->data['status'];
    }

    /**
     * Get `status_name` property
     *
     * @since 3.0.0
     *
     * @return string
     */
    public function get_status_name() {
        $status_name = dokan_pro()->refund->get_status_names();
        return $status_name[ $this->get_status() ];
    }

    /**
     * Get `method` property
     *
     * @since 3.0.0
     *
     * @return string
     */
    public function get_method() {
        return $this->data['method'];
    }

    /**
     * Prepare model for DB insertion
     *
     * @since 3.0.0
     * @since 3.4.2 Refund method changed to `1` for API, `0` for manual.
     *
     * @return array
     */
    protected function prepare_for_db() {
        $data = $this->get_data();

        $data['item_qtys']       = is_array( $data['item_qtys'] ) ? wp_json_encode( $data['item_qtys'] ) : null;
        $data['item_totals']     = is_array( $data['item_totals'] ) ? wp_json_encode( $data['item_totals'] ) : null;
        $data['item_tax_totals'] = is_array( $data['item_tax_totals'] ) ? wp_json_encode( $data['item_tax_totals'] ) : null;

        // we are setting WC provided method `true` or `false` to `1` or `0`
        $data['method'] = dokan_validate_boolean( $data['method'] ) ? '1' : '0';

        return $data;
    }

    /**
     * Save a model
     *
     * @since 3.0.0
     *
     * @return Refund|WP_Error
     */
    public function save() {
        if ( ! $this->get_id() ) {
            return $this->create();
        } else {
            return $this->update();
        }
    }

    /**
     * Create a model
     *
     * @since 3.0.0
     *
     * @return Refund|WP_Error
     */
    protected function create() {
        global $wpdb;

        unset( $this->data['id'] );

        $data = $this->prepare_for_db();

        $inserted = $wpdb->insert(
            $wpdb->dokan_refund,
            $data,
            [ '%d', '%d', '%f', '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s' ]
        );

        if ( $inserted !== 1 ) {
            return new WP_Error( 'dokan_refund_create_error', __( 'Could not create new refund', 'dokan' ) );
        }

        $refund = dokan_pro()->refund->get( $wpdb->insert_id );

        /**
         * Fires after created a refund request
         *
         * @since 3.0.0
         *
         * @param Refund $refund
         */
        do_action( 'dokan_refund_request_created', $refund );

        return $refund;
    }

    /**
     * Update a model
     *
     * @since 3.0.0
     *
     * @return Refund|WP_Error
     */
    protected function update() {
        global $wpdb;

        $data = $this->prepare_for_db();

        $updated = $wpdb->update(
            $wpdb->dokan_refund,
            [
                'order_id'        => $data['order_id'],
                'seller_id'       => $data['seller_id'],
                'refund_amount'   => $data['refund_amount'],
                'refund_reason'   => $data['refund_reason'],
                'item_qtys'       => $data['item_qtys'],
                'item_totals'     => $data['item_totals'],
                'item_tax_totals' => $data['item_tax_totals'],
                'restock_items'   => $data['restock_items'],
                'date'            => $data['date'],
                'status'          => $data['status'],
                'method'          => $data['method'],

            ],
            [ 'id' => $this->get_id() ],
            [ '%d', '%d', '%f', '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s' ],
            [ '%d' ]
        );

        if ( false === $updated ) {
            return new WP_Error( 'dokan_refund_update_error', __( 'Could not update refund', 'dokan' ) );
        }

        /**
         * Action based on refund status
         *
         * @since 3.0.0
         *
         * @param Refund $this
         */
        do_action( 'dokan_refund_request_' . dokan_pro()->refund->get_status_name( $this->get_status() ), $this );

        /**
         * Fires after update a refund
         *
         * @since 3.0.0
         *
         * @param Refund $this
         */
        do_action( 'dokan_refund_updated', $this );

        return $this;
    }

    /**
     * Delete a model
     *
     * @since 3.0.0
     *
     * @return Refund|WP_Error
     */
    public function delete() {
        global $wpdb;

        $deleted = $wpdb->delete(
            $wpdb->dokan_refund,
            [ 'id' => $this->data['id'] ],
            [ '%d' ]
        );

        if ( ! $deleted ) {
            return new WP_Error( 'dokan_pro_refund_error_delete', __( 'Could not delete refund request', 'dokan' ) );
        }

        /**
         * Fires after delete a refund
         *
         * @since 3.0.0
         *
         * @param Refund $this
         */
        do_action( 'dokan_pro_refund_deleted', $this );

        return $this;
    }

    /**
     * Approve a refund
     *
     * @since 3.0.0
     *
     * @param array $args
     *
     * @throws Exception
     *
     * @return Refund|WP_Error
     */
	public function approve( $args = [] ) {
		$order = wc_get_order( $this->get_order_id() );

		// Validate refund can be approved
		$validation_result = $this->validate_refund_approval( $order );

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

		// Prepare refund parameters
		$refund_params = $this->prepare_refund_params( $order );

		// Remember the status so a rolled back refund can restore it and stay retryable.
		$status_before_refund = $order->get_status();

		$is_sub_order = dokan_is_sub_order( $order->get_id() );

		// For sub orders, defer restocking until the parent (gateway) refund succeeds, so a failed gateway refund can be retried without restocking twice.
		$refund = $this->process_wc_refund( $is_sub_order ? array_merge( $refund_params, [ 'restock_items' => false ] ) : $refund_params );

		if ( is_wp_error( $refund ) ) {
			// Leave the request pending so the admin can retry; a gateway can refuse for reasons that clear later, e.g. Authorize.Net only credits a settled transaction.
			return $this->refund_processing_failed( $order, $refund );
		}

		// Handle parent order refund if this is a sub-order
		if ( $is_sub_order ) {
			$parent_refund_result = $this->process_parent_order_refund( $order, $refund_params );

			if ( is_wp_error( $parent_refund_result ) ) {
				// Roll back the sub-order refund and keep the request pending instead of cancelling it.
				wc_delete_shop_order_transients( $order->get_id() );
				$refund->delete( true );
				$this->restore_order_status( $order->get_id(), $status_before_refund );

				return $this->refund_processing_failed( $order, $parent_refund_result );
			}

			// Run the deferred restock now that the gateway refund went through.
			if ( $refund_params['restock_items'] ) {
				wc_restock_refunded_items( $order, $refund_params['line_items'] );
			}
		}

        // add vendor refund id to current refund
        $args['refund_order_id'] = $refund->get_id();

		// Add refund note to the order
		$this->add_refund_note( $order, $refund, $refund_params['api_refund'] );

		// Calculate vendor refund amount
		$vendor_earning_refund = apply_filters( 'dokan_vendor_earning_in_refund', $refund, $order );

		/**
		 * Filter the vendor payout share of the refund. Gateway integrations
		 * hook here to deduct their fee share from the vendor payout.
		 *
		 * @since 3.3.0 add filter dokan_refund_approve_vendor_refund_amount
		 *
		 * @param float $vendor_earning_refund vendor refund amount
		 * @param Refund $refund
		 * @param array $args
		 */
		$vendor_payout_refund = apply_filters( 'dokan_refund_approve_vendor_refund_amount', $vendor_earning_refund, $args, $this );

		/**
		 * @since 3.3.0
		 * @since 5.0.8 Added $vendor_earning_refund param; $vendor_payout_refund was $vendor_refund.
		 *
		 * @param Refund $this
		 * @param array $args
		 * @param float $vendor_payout_refund  The vendor refund amount after the gateway fee is deducted.
		 * @param float $vendor_earning_refund The vendor refund amount before the gateway fee deduction.
		 */
		do_action( 'dokan_refund_approve_before_insert', $this, $args, $vendor_payout_refund, $vendor_earning_refund );

        // Update vendor balance
        $this->update_vendor_balance( $vendor_payout_refund, $args, $refund, $order, $vendor_earning_refund );

		// Update order table with new refund amount
		$this->update_order_amounts( $vendor_payout_refund, $refund, $order );

		// Add approval note
		$approved_by = $this->get_approver_name();
		$order->add_order_note(
            sprintf(
                // translators: %1$s is the name of the user who approved the refund.
                __( 'Refund request approved by %1$s', 'dokan' ),
                $approved_by
            )
		);

		// Update refund status and save
		$this->set_status( dokan_pro()->refund->get_status_code( 'completed' ) );
		$refund_result = $this->save();

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

		/**
		 * Fires after approve a refund request
		 *
		 * @since 3.0.0
		 * @since 3.3.0 added $args and $vendor_refund param
		 * @since 5.0.8 Added $vendor_earning_refund param; $vendor_payout_refund was $vendor_refund.
		 *
		 * @param Refund $refund
		 * @param array $args
		 * @param float $vendor_payout_refund  The vendor refund amount after the gateway fee is deducted.
		 * @param float $vendor_earning_refund The vendor refund amount before the gateway fee deduction.
		 */
		do_action( 'dokan_pro_refund_approved', $this, $args, $vendor_payout_refund, $vendor_earning_refund );

		return $this;
	}

	/**
	 * Handle a failed WC/gateway refund without cancelling the request.
	 *
	 * @since 5.0.12
	 *
	 * @param WC_Order $order Vendor order the refund request belongs to.
	 * @param WP_Error $error Error returned while processing the refund.
	 *
	 * @return WP_Error
	 */
	private function refund_processing_failed( $order, $error ) {
		$reason = trim( wp_strip_all_tags( $error->get_error_message() ) );
		$reason = $reason ? $reason : __( 'The payment gateway rejected the refund.', 'dokan' );

		// Keep the gateway's reason on the order so the failure stays auditable.
		$order->add_order_note(
			sprintf(
				// translators: %1$d is the refund request ID, %2$s is the error reported while processing the refund.
				__( 'Refund request #%1$d could not be processed and is still awaiting approval. Reason: %2$s', 'dokan' ),
				$this->get_id(),
				$reason
			)
		);

		return new WP_Error(
			'dokan_pro_refund_error_processing',
			// translators: %s is the error reported by the payment gateway.
			sprintf( __( 'This refund failed to process. %s', 'dokan' ), $reason ),
			[ 'status' => 400 ]
		);
	}

	/**
	 * Restore the vendor order status after a rolled back refund.
	 *
	 * @since 5.0.12
	 *
	 * @param int    $order_id        Vendor order ID.
	 * @param string $previous_status Status the order had before the refund attempt.
	 *
	 * @return void
	 */
	private function restore_order_status( $order_id, $previous_status ) {
		// Re-read the order: wc_create_refund() may have moved its own instance to refunded.
		$order = wc_get_order( $order_id );

		if ( ! $order || $order->get_status() === $previous_status ) {
			return;
		}

		$order->update_status( $previous_status, __( 'Order status restored after a failed refund attempt.', 'dokan' ) );
	}

	/**
	 * Validate if refund can be approved
	 *
     * @param \WC_Order|false $order
     *
	 * @return true|WP_Error
	 */
	private function validate_refund_approval( $order ) {
		if ( ! dokan_pro()->refund->is_approvable( $this->get_order_id() ) ) {
			return new WP_Error( 'dokan_pro_refund_error_approve', __( 'This refund is not allowed to approve', 'dokan' ), [ 'status' => 400 ] );
		}

		if ( ! $order ) {
			return new WP_Error( 'dokan_pro_refund_error_approve', __( 'Could not find order', 'dokan' ), [ 'status' => 400 ] );
		}

		return true;
	}

	/**
	 * Prepare parameters for refund processing
	 *
	 * @param WC_Order $order
	 * @return array
	 */
	private function prepare_refund_params( $order ) {
		$api_refund = dokan_validate_boolean( $this->get_method() );
		$restock_refunded_items = dokan_validate_boolean( $this->get_restock_items() );

		// Prepare line items for refund
		$line_items = $this->prepare_line_items( $order );

		/**
		 * Set auto process API refund for the main order.
		 *
		 * @since 3.3.7
		 *
		 * @param bool $api_refund
		 * @param Refund $refund
		 */
		$api_refund = apply_filters( 'dokan_pro_auto_process_api_refund', $api_refund, $this );

		return [
			'amount' => $this->get_refund_amount(),
			'reason' => $this->get_refund_reason(),
			'order_id' => $order->get_id(),
			'line_items' => $line_items,
			'refund_payment' => $order->get_parent_id() ? false : $api_refund,
			'restock_items' => $restock_refunded_items,
			'api_refund' => $api_refund,
		];
	}

	/**
	 * Prepare line items for refund
	 *
	 * @param WC_Order $order
	 * @return array
	 */
	private function prepare_line_items( $order ) {
		$line_items = [];
		$item_ids = array_unique( array_merge( array_keys( $this->get_item_qtys(), $this->get_item_totals(), true ) ) );

		// Initialize line items
		foreach ( $item_ids as $item_id ) {
			$line_items[ $item_id ] = [
				'qty' => 0,
				'refund_total' => 0,
				'refund_tax' => [],
			];
		}

		// Set quantities
		foreach ( $this->get_item_qtys() as $item_id => $qty ) {
			$line_items[ $item_id ]['qty'] = max( $qty, 0 );
		}

		// Set refund totals
		foreach ( $this->get_item_totals() as $item_id => $total ) {
			$item = $order->get_item( $item_id );
			$line_items[ $item_id ]['refund_total'] = wc_format_decimal( $total );
		}

		// Set tax totals
		foreach ( $this->get_item_tax_totals() as $item_id => $tax_totals ) {
			foreach ( $tax_totals as $total_tax_key => $total_tax ) {
				$line_items[ $item_id ]['refund_tax'][ $total_tax_key ] = wc_format_decimal( $total_tax );
			}
		}

		return $line_items;
	}

	/**
	 * Process the refund through WooCommerce
	 *
	 * @param array $params
	 * @return \WC_Order_Refund|WP_Error
	 */
	private function process_wc_refund( $params ) {
		$refund_args = [
			'amount' => $params['amount'],
			'reason' => $params['reason'],
			'order_id' => $params['order_id'],
			'line_items' => $params['line_items'],
			'refund_payment' => $params['refund_payment'],
			'restock_items' => $params['restock_items'],
		];

		$refund = wc_create_refund( $refund_args );

		if ( is_wp_error( $refund ) ) {
            // translators: %1$d is the order ID for which the refund processing failed, %2$s is the gateway error.
            dokan_log( sprintf( __( 'Refund processing for the order #%1$d failed. %2$s', 'dokan' ), $params['order_id'], $refund->get_error_message() ) );
		}

		return $refund;
	}

	/**
	 * Process parent order refund for sub-orders
	 *
	 * @param WC_Order $order
	 * @param array $params
	 * @return WC_Order_Refund|WP_Error
	 */
	private function process_parent_order_refund( $order, $params ) {
		// Remove hooks to prevent unnecessary recalculation
		remove_action( 'woocommerce_order_status_changed', [ dokan()->fees, 'calculate_gateway_fee' ], 100 );

		// Map line items from sub-order to parent order
		$parent_line_items = $this->map_parent_order_line_items( $order, $params['line_items'] );

		// Create parent order refund
		$parent_refund = wc_create_refund(
            [
				'amount' => $params['amount'],
				'reason' => $params['reason'],
				'order_id' => $order->get_parent_id(),
				'line_items' => $parent_line_items,
				'refund_payment' => $params['api_refund'],
				'restock_items' => true,
            ]
        );

		// Restore hooks
		add_action( 'woocommerce_order_status_changed', [ dokan()->fees, 'calculate_gateway_fee' ], 100 );

		if ( is_wp_error( $parent_refund ) ) {
            // translators: %1$d is the suborder ID for which the refund processing failed, %2$s is the gateway error.
            dokan_log( sprintf( __( 'Refund processing for the suborder #%1$d failed. %2$s', 'dokan' ), $order->get_id(), $parent_refund->get_error_message() ) );
			return $parent_refund;
		}

		// Add parent order note
		$parent_order = wc_get_order( $order->get_parent_id() );
		$approved_by = $this->get_approver_name();
		$payment_method_title = $parent_order->get_payment_method_title();

		$parent_order->add_order_note(
            sprintf(
                // translators: %1$s is the payment method title, %2$s is the refund reason, %3$d is the suborder ID, %4$s is the name of the user who approved the refund.
                __( 'Refund Processed via %1$s – Reason: %2$s - Suborder %3$d - Approved by %4$s', 'dokan' ),
                $params['api_refund'] ? $payment_method_title : __( 'Manual Processing', 'dokan' ),
                $parent_refund->get_reason(),
                $order->get_id(),
                $approved_by
            )
		);

		return $parent_refund;
	}

	/**
	 * Map line items from sub-order to parent order
	 *
	 * @param WC_Order $order
	 * @param array $line_items
	 * @return array
	 */
	private function map_parent_order_line_items( $order, $line_items ) {
		$line_items_product_map = [];
		foreach ( $order->get_items( 'line_item' ) as $item ) {
			if ( ! array_key_exists( $item->get_id(), $line_items ) ) {
				continue;
			}
			$line_items_product_map[ $item['product_id'] ] = $item->get_id();
		}

		$parent_order = wc_get_order( $order->get_parent_id() );
		$parent_line_items = [];

		foreach ( $parent_order->get_items( 'line_item' ) as $item ) {
			if ( ! array_key_exists( $item['product_id'], $line_items_product_map ) ) {
				continue;
			}

			$line_item_id = $line_items_product_map[ $item['product_id'] ];
			$parent_line_items[ $item->get_id() ] = $line_items[ $line_item_id ];
		}

		return $parent_line_items;
	}

	/**
	 * Add refund note to the order
	 *
	 * @param WC_Order $order
	 * @param WC_Order_Refund $refund
	 * @param bool $api_refund
	 */
	private function add_refund_note( $order, $refund, $api_refund ) {
		$payment_method_title = $order->get_payment_method_title();

		$order->add_order_note(
            sprintf(
                // translators: %1$s is the refunded amount, %2$s is the payment method, %3$s is the refund reason.
                __( 'Refunded %1$s via %2$s – Reason: %3$s ', 'dokan' ),
                $refund->get_formatted_refund_amount(),
                $api_refund ? $payment_method_title : __( 'Manual Processing', 'dokan' ),
                $refund->get_reason()
            )
		);
	}

	/**
	 * Update vendor balance with refund amount
	 *
	 * @param float            $vendor_payout_refund  The vendor refund amount after the gateway fee is deducted.
	 * @param array            $args
	 * @param \WC_Order_Refund $refund_order
	 * @param \WC_Order        $order
	 * @param float            $vendor_earning_refund The vendor refund amount before the gateway fee deduction.
	 */
	private function update_vendor_balance( $vendor_payout_refund, $args, $refund_order, $order, $vendor_earning_refund ) {
		/**
		 * @since 3.3.2 filter dokan_refund_insert_into_vendor_balance added
		 * @since 5.0.8 Added $vendor_earning_refund param; $vendor_payout_refund was $vendor_refund.
		 *
		 * @param bool true return false if you don't want to insert into vendor balance table
		 * @param Refund $this
		 * @param array $args
		 * @param float $vendor_payout_refund  The vendor refund amount after the gateway fee is deducted.
		 * @param float $vendor_earning_refund The vendor refund amount before the gateway fee deduction.
		 */
		if ( apply_filters( 'dokan_refund_insert_into_vendor_balance', true, $this, $args, $vendor_payout_refund, $vendor_earning_refund ) ) {
            do_action( 'dokan_refund_adjust_vendor_balance', $vendor_payout_refund, $refund_order, $order, $vendor_earning_refund );
		}
	}

	/**
	 * Update order table with new refund amount
	 *
     * @param float $vendor_refund
	 * @param \WC_Order_Refund $refund_order
	 * @param \WC_Order $order
	 */
	private function update_order_amounts( $vendor_refund, $refund_order, $order ) {
        do_action( 'dokan_refund_adjust_dokan_orders', $vendor_refund, $refund_order, $order );
	}

	/**
	 * Get name of user who approved the refund
	 *
	 * @return string
	 */
	private function get_approver_name() {
		$current_user = is_user_logged_in() ? wp_get_current_user() : '';
		return ! empty( $current_user ) ? $current_user->get( 'user_nicename' ) : 'admin';
	}


    /**
     * Cancel a refund request
     *
     * @since 3.0.0
     * @since 3.3.6 Adding Order note to suborder and parent order.
     *
     * @return Refund|WP_Error
     */
    public function cancel() {
        $this->set_status( dokan_pro()->refund->get_status_code( 'cancelled' ) );

        $refund = $this->save();
        if ( is_wp_error( $refund ) ) {
            return $refund;
        }

        $order = wc_get_order( $refund->get_order_id() );
        if ( ! $order ) {
            return new WP_Error( 'dokan_refund_order_not_found', __( 'Order not found', 'dokan' ) );
        }

        $order_id = $order->get_id();

        $order->add_order_note(
            sprintf(
            // translators: 1: Refund amount 2: Refund reason.
                __( 'Refund Request for the amount: %1$s – Reason: %2$s - Got canceled.', 'dokan' ),
                $refund->get_refund_amount(),
                $refund->get_refund_reason()
            )
        );

        if ( $order->get_parent_id() ) {
            $parent_order = wc_get_order( $order->get_parent_id() );

            if ( $parent_order ) {
                $parent_order->add_order_note(
                    sprintf(
                    // translators: 1: Suborder ID 2: Refund amount 2: Refund reason.
                        __( 'Refund Request for the Suborder #%1$s - Amount %2$s – Reason: %3$s - Got canceled.', 'dokan' ),
                        $order_id,
                        $refund->get_refund_amount(),
                        $refund->get_refund_reason()
                    )
                );
            }
        }

        /**
         * Fires after cancel a refund request
         *
         * @since 3.0.0
         *
         * @param Refund $this
         */
        do_action( 'dokan_pro_refund_cancelled', $this );

        return $this;
    }

    /**
     * Check if refund is via API.
     *
     * @since 3.4.2
     *
     * @return bool
     */
    public function is_via_api() {
        return wc_string_to_bool( $this->get_method() );
    }

    /**
     * Check if refund is manual.
     *
     * @since 3.4.2
     *
     * @return bool
     */
    public function is_manual() {
        return wc_string_to_bool( $this->get_method() ) === false;
    }
}
