<?php

class APP_Balanced_Payments_Gateway extends APP_Payment_Gateway implements APP_Instant_Payment_Processor, APP_Escrow_Payment_Processor {
	private $marketplace = null;

	public function __construct() {
		parent::__construct( 'balanced-payments', array(
			'dropdown' => __( 'Credit Card', 'appthemes-balanced-payments' ),
			'admin'    => __( 'Balanced Payments', 'appthemes-balanced-payments' )
		) );

		add_action( 'wp_enqueue_scripts', array( $this, 'register_scripts' ) );
		add_action( 'appthemes_escrow_after_settings_form', array( $this, 'after_user_form' ) );

		add_action( 'wp_ajax_app_bp_instant_complete_order', array( $this, 'do_ajax_payment' ) );
		add_action( 'wp_ajax_app_bp_save_bank_account', array( $this, 'do_bank_save' ) );

	}

	public function form () {
		return array(
			array(
				'title'  => __( 'Marketplace Information', 'appthemes-balanced-payments' ),
				'fields' => array(
					array(
						'type'  => 'text',
						'name'  => 'api_key',
						'title' => __( 'API Key' ),
						'desc'  => __( 'The API key for the marketplace you are using. Can be a test or production marketplace.', 'appthemes-balanced-payments' ),
					)
				)
			),
			array(
				'title'  => __( 'Processing Instructions', 'appthemes-balanced-payments' ),
				'fields' => array(
					array(
						'type'  => 'checkbox',
						'name'  => 'validate_cvv',
						'title' => __( 'Validate CVV', 'appthemes-balanced-payments' ),
						'desc'  => __( 'If enabled, transactions will not be processed if an invalid CVV has been entered.', 'appthemes-balanced-payments' ),
					),
					array(
						'type'  => 'checkbox',
						'name'  => 'validate_avs',
						'title' => __( 'Validate Address', 'appthemes-balanced-payments' ),
						'desc'  => __( 'If enabled, transactions will not be processed if an invalid postal code has been entered. This is unreliable outside the US.', 'appthemes-balanced-payments' ),
					),
				)
			)
		);
	}

	public function process ( $order, array $options ) {

		$is_ok = $this->setup_marketplace();
		if ( ! $is_ok ) {
			return false;
		}

		include dirname( __FILE__ ) . '/payment-form.php';

	}

	public function do_ajax_payment() {

		if ( empty( $_GET['order_id'] ) ) {
			wp_send_json_error( array( 'message' => __( 'No order id included.', 'appthemes-balanced-payments' ) ) );
		}

		if ( empty( $_GET['uri'] ) ) {
			wp_send_json_error( array( 'message' => __( 'No funding instrument included.', 'appthemes-balanced-payments' ) ) );
		}

		$this->setup_marketplace();
		$options = APP_Gateway_Registry::get_gateway_options( 'balanced-payments' );
		$owner   = Balanced\Marketplace::mine()->owner_customer;

		$order_id = intval( $_GET['order_id'] );
		$order    = appthemes_get_order( $order_id );

		if ( ! $order ) {
			wp_send_json_error( array( 'messaage' => __( 'Bad order given.', 'appthemes-balanced-payments' ) ) );
		}

		try {

			$card   = Balanced\Card::get( $_GET['uri'] );
			$errors = $this->do_card_checks( $card, $options );
			if ( ! empty( $errors ) ) {
				wp_send_json_error( $errors );
			}
			$this->associate_card( $card );

			$order_info = array(
				'description' => $order->get_description(),
			);

			if ( $order instanceof APP_Instant_Order ) {
				$bp_order = $owner->orders->create( $order_info );
			} else if ( $order instanceof APP_Escrow_Order ) {
				$seller = Balanced\Customer::get( $this->get_seller_customer( $order ) );
				$bp_order = $seller->orders->create( $order_info );
			}

			$this->debit_card( $bp_order, $card, $order );

			if ( $order instanceof APP_Instant_Order ) {
				$this->complete_instant_order( $bp_order, $order );
			} else if ( $order instanceof APP_Escrow_Order ) {
				$this->transition_escrow_order( $bp_order, $order );
			}

		} catch ( Exception $e ){
			$data = array(
				'type' => 'Internal Error',
			);

			if( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
				$data = array(
					'type'      => 'Internal Error',
					'bp-errors' => $e->response->body->errors,
					'file'      => $e->getFile(),
					'line'      => $e->getLine(),
				);
			}

			$errors = $e->response->body->errors;
			if( isset( $errors[0] ) ) {
				$error = $errors[0];

				if ( 'card-declined' == $error->category_code ) {
					$data['message'] = __( 'This transaction was declined by the card issuer. Please contact your bank.', 'appthemes-balanced-payments' );
				}

				$message = sprintf( __( 'Error Occurred: %s', 'appthemes-balanced-payments'), $error->description );
				$order->log( $message, 'minor' );
			} else {
				$order->log( __('Error Occurred: Unknown internal error.', 'appthemes-balanced-payments' ), 'minor' );
			}

			wp_send_json_error( $data );
		}

		wp_send_json_success();
		die();
	}

	private function get_seller_customer ( APP_Escrow_Order $order ) {
		foreach ( $order->get_receivers() as $user => $amount ) {
			if ( $user != 0 ) {
				return get_user_option( 'bp-customer-id', $user );
			}
		}
	}

	private function debit_card ( Balanced\Order $bp_order, Balanced\Card $card, $order ) {

		$description = $order->get_description();
		$statement_description = substr( $order->get_description(), 0, 22 );

		$debit_info = $bp_order->debitFrom( $card, $order->get_total() * 100, $statement_description, $description );

		$order->add_data( 'bp-order', $bp_order->id );
		$order->log( sprintf( __( 'Successfully debited card. (ID: %s)', 'appthemes-balanced-payments'), $debit_info->id ) );

	}

	public function process_escrow ( APP_Escrow_Order $order, array $options ) {

		$is_ok = $this->setup_marketplace();
		if ( ! $is_ok ) {
			return false;
		}

		include dirname( __FILE__ ) . '/payment-form.php';

	}

	private function transition_escrow_order( Balanced\Order $bp_order, APP_Escrow_Order $order ) {
		$order->paid();
		wp_send_json_success();
	}

	public function complete_escrow ( APP_Escrow_Order $order, array $options ) {
		$this->setup_marketplace();

		$bp_order_id = $order->get_data( 'bp-order' );
		$bp_order = Balanced\Order::get( $bp_order_id );

		foreach ( $order->get_receivers() as $user_id => $amount ) {
			$amount = ( (double) $amount ) * 100;
			$this->credit_bank( $bp_order, $amount, $order, $user_id );
		}

		$bp_order->refresh();
		$this->credit_bank( $bp_order, $bp_order->amount_escrowed, $order, 0 );
		$order->complete();
	}

	public function fail_escrow ( APP_Escrow_Order $order, array $options ) {
		$this->setup_marketplace();

		$bp_order_id = $order->get_data( 'bp-order' );
		$bp_order = Balanced\Order::get( $bp_order_id );

		foreach ( $bp_order->debits as $debit ) {
			$debit->refunds->create( array(
				'description' => sprintf( __( 'Refund for Order #%d', 'appthemes-balanced-payments' ), $order->get_id() )
			) );
			$order->log( 'Refunded to original payment method.' );
		}

		$order->refunded();
		return true;
	}

	public function user_form () {

		$token = get_user_option( 'bp-token', get_current_user_id() );

		if ( empty( $token ) ) {
			$fields = array(
				array(
					'title' => __( 'Account Holder', APP_TD ),
					'type' => 'text',
					'name' => 'bp-account-holder',
					'extra' => array(
						'autocomplete' => 'off',
						'class'        => 'text required regular-text',
						'placeholder'  => 'John Doe',
					),
					'desc' => __( 'The name on the account.', APP_TD ),
				),
				array(
					'title' => __( 'Routing Number', APP_TD ),
					'type' => 'text',
					'name' => 'bp-account-routing',
					'extra' => array(
						'autocomplete' => 'off',
						'class'        => 'text required regular-text digits isRoutingNumberValid',
						'placeholder'  => '322271627',
					),
					'desc' => __( 'This is usually the first 9 digits on your physical check.', APP_TD ),
				),
				array(
					'title' => __( 'Account Number', APP_TD ),
					'type' => 'text',
					'name' => 'bp-account-number',
					'extra' => array(
						'autocomplete' => 'off',
						'class'        => 'text required regular-text digits',
						'placeholder'  => '9900000000',
					),
					'desc' => __( 'This is usually the next 10 digits on your physical check.', APP_TD ),
				),
				array(
					'title' => __( 'Account ID', APP_TD ),
					'type' => 'text',
					'name' => 'bp-token',
					'extra' => array(
						'class' => '',
						'disabled' => 'disabled',
					),
					'desc' => __( 'This ID will be auto-generated after your bank details are safely stored.', APP_TD ),
				)
			);
		} else {
			$fields = array(
				array(
					'title' => __( 'Account ID', APP_TD ),
					'type' => 'text',
					'name' => 'bp-token',
					'extra' => array(
						'class' => '',
					),
					'desc' => __( 'Removing this ID and saving will allow you to enter new bank account details.', APP_TD ),
				)
			);
		}

		return array(
			'title'  => __( 'Balance Payments', 'appthemes-balanced-payments' ),
			'fields' => $fields
		);
	}

	public function after_user_form( $vars ) {
		echo '<script type="text/javascript">window.ap_bp_ajaxurl="' . admin_url( 'admin-ajax.php' )  . '";</script>';
		echo '<script type="text/javascript" src="https://js.balancedpayments.com/1.1/balanced.js"></script>';
		echo '<script type="text/javascript" src="' . plugins_url( 'setting-form.js', __FILE__ )  .'"></script>';
	}

	public function do_bank_save() {

		if ( empty( $_GET['uri'] ) ) {
			wp_send_json_error( array( 'message' => __( 'No bank account included.', 'appthemes-balanced-payments' ) ) );
		}

		$this->setup_marketplace();
		$options = APP_Gateway_Registry::get_gateway_options( 'balanced-payments' );

		$bank_account = Balanced\BankAccount::get( $_GET['uri'] );
		$this->associate_bank( $bank_account );

		update_user_option( get_current_user_id(), 'bp-token', $bank_account->id );
		wp_send_json_success();
	}

	private function complete_instant_order ( Balanced\Order $bp_order, APP_Instant_Order $order ) {

		$bank_account = Balanced\Marketplace::mine()->owner_customer->bank_accounts->query()->first();

		$order_total = $order->get_total() * 100;

		$deposit_description = sprintf( __( 'Transaction #%d', 'appthemes-balanced-payments' ), $order->get_id() );
		$statement_description = substr( $order->get_description(), 0, 22 );

		$credit_info = $bp_order->creditTo( $bank_account, $order_total, $statement_description );
		$order->log( sprintf( __( 'Successfully credited owner. (ID: %s)', 'appthemes-balanced-payments'), $credit_info->id ) );

		$order->complete();
	}

	private function do_card_checks( Balanced\Card $card, array $options ) {

		if ( $options['validate_cvv'] ) {
			if ( $card->cvv_match === 'no' ) {
				return array( 'message' => __( 'The CVV value could not be matched.', 'appthemes-balanced-payments' ) ) ;
			} else if ( $card->cvv_match === 'unsupported' ) {
				return array( 'message' => __( 'The CVV value is not supported.', 'appthemes-balanced-payments' ) ) ;
			}
		}

		if ( $options['validate_avs'] ) {
			if ( $card->avs_postal_match === 'no' ) {
				return array( 'message' => __( 'The postal code value could not be matched.', 'appthemes-balanced-payments' ) ) ;
			} else if ( $card->avs_postal_match === 'unsupported' ) {
				return array( 'message' => __( 'The postal code value is not supported.', 'appthemes-balanced-payments' ) ) ;
			}
		}

		return array();
	}

	public function register_scripts(){

		if ( ! is_singular( APPTHEMES_ORDER_PTYPE ) ) {
			return;
		}

		wp_enqueue_style( "balanced", plugins_url('style.css', __FILE__)  );
		wp_enqueue_script( 'jquery' );
		wp_enqueue_script( 'balanced-payments', 'https://js.balancedpayments.com/1.1/balanced.js' );
		wp_enqueue_script( 'balanced-payments-handler', plugin_dir_url( __FILE__ ) . 'form.js',
			array( 'validate', 'balanced-payments' ) );

		wp_localize_Script( 'balanced-payments-handler', 'app_bp',
			array(
				'ajax_url'            => preg_replace('/\?.*/', '', admin_url( 'admin-ajax.php' ) ),
				'submit_text'         => __( 'Submit', 'appthemes-balanced-payments' ),
				'processing_text'     => __( 'Submitting...', 'appthemes-balanced-payments' ),
				'required_text'       => __( 'This field is required.', 'appthemes-balanced-payments' ),
				'invalid_digits_text' => __( 'Please only enter digits.', 'appthemes-balanced-payments' ),
				'invalid_cc_text'     => __( 'Please enter a valid credit card number.', 'appthemes-balanced-payments' ),
				'invalid_cvv_text'    => __( 'Please enter a valid CVV.', 'appthemes-balanced-payments' ),
				'invalid_expiry_text' => __( 'Please enter a valid expiration date.', 'appthemes-balanced-payments' ),
			));

	}

	private function setup_marketplace() {

		$options = APP_Gateway_Registry::get_gateway_options( 'balanced-payments' );

		$this->api_key = $options['api_key'];
		$is_valid      = $this->validate_api_key( $this->api_key );
		if ( ! $is_valid ) {
			return $this->display_config_error(
				__( 'Invalid API Key Given.', 'appthemes-balanced-payments')
			);
		}

		Balanced\Settings::$api_key = $options['api_key'];
		$this->marketplace = Balanced\Marketplace::mine();

		return true;
	}

	private function create_new_user( WP_User $user ) {
		$customer = $this->marketplace->customers->create( array(
			'name'   => $user->display_name,
			'email'  => $user->user_email,
			'meta'   => array(
				'Login' => $user->user_login,
			),
		) );

		$customer_id = $customer->href;
		update_user_meta( $user->ID, 'bp-customer-id', $customer_id );

		return $customer_id;
	}

	private function associate_card( Balanced\Card $card ) {

		$current_user = wp_get_current_user();
		$customer_id  = get_user_meta( $current_user->ID, 'bp-customer-id', true );
		if( empty( $customer_id ) ) {
			$customer_id = $this->create_new_user( $current_user );
		}

		$card->associateToCustomer( $customer_id );

	}

	private function associate_bank( Balanced\BankAccount $bank_account ) {

		$current_user = wp_get_current_user();
		$customer_id  = get_user_meta( $current_user->ID, 'bp-customer-id', true );
		if( empty( $customer_id ) ) {
			$customer_id = $this->create_new_user( $current_user );
		}

		$bank_account->associateToCustomer( $customer_id );

	}

	private function credit_bank ( Balanced\Order $bp_order, $amount, $order, $user_id ) {

		if ( $user_id == 0 ) {
			$bank_account = Balanced\Marketplace::mine()->owner_customer->bank_accounts->query()->first();
		} else {
			$bp_token = get_user_option( 'bp-token', $user_id );
			$bank_account = Balanced\BankAccount::get( $bp_token );
		}

		$deposit_description = sprintf( __( 'Transaction #%d', 'appthemes-balanced-payments' ), $order->get_id() );
		$statement_description = substr( $order->get_description(), 0, 22 );

		$credit_info = $bp_order->creditTo( $bank_account, $amount, $statement_description );
		$order->log( sprintf( __( 'Successfully credited user %d amount of %s. (ID: %s)', 'appthemes-balanced-payments'), $user_id, appthemes_get_price( $amount/100 ), $credit_info->id ) );	

	}

	private function validate_api_key( $api_key ) {

		$is_valid = true;
		if ( empty ( $api_key ) ) {
			$is_valid = false;
		}

		return $is_valid;
	}

	// @todo display general message for non-admin users
	private function display_config_error( $error_message ) {

		$config_message = __( 'Gateway Configuration Error: %s', 'appthemes-balanced-payments' );
		printf( $config_message, $error_message );

	}

}