<?php

namespace StoreEngine\Addons\Paddle;

use StoreEngine\Classes\Order;
use StoreEngine\Payment_Gateways;
use StoreEngine\Utils\Formatting;
use WP_Error;

final class PaddleService {

	protected bool $is_live = false;

	protected string $client_token = '';
	protected string $api_key = '';

	protected string $currency = 'USD';

	/**
	 * List of currencies supported by Paddle.
	 *
	 * @link https://developer.paddle.com/concepts/sell/supported-currencies#supported-payments
	 *
	 * @var array|string[]
	 */
	protected static array $supported_currencies = [
		'USD',
		'EUR',
		'GBP',
		'ARS',
		'AUD',
		'BRL',
		'CAD',
		'CHF',
		'CNY',
		'COP',
		'CZK',
		'DKK',
		'HKD',
		'HUF',
		'ILS',
		'INR',
		'JPY',
		'KRW',
		'MXN',
		'NOK',
		'NZD',
		'PLN',
		'RUB',
		'SEK',
		'SGD',
		'THB',
		'TRY',
		'TWD',
		'UAH',
		'VND',
		'ZAR',
	];

	/**
	 * List of currencies supported by Paddle that has no decimals
	 *
	 * @link https://developer.paddle.com/concepts/sell/supported-currencies#supported-payments
	 *
	 * @var array|string[]
	 */
	protected static array $zero_decimal_currencies = [
		'JPY',
		'KRW',
		'VND',
	];

	protected static array $currency_minimum_charges = [
		'USD' => 0.70,
		'EUR' => 0.65,
		'GBP' => 0.55,
		'ARS' => 780.00,
		'AUD' => 1.10,
		'BRL' => 4.00,
		'CAD' => 1.00,
		'CHF' => 0.60,
		'CNY' => 5.10,
		'COP' => 2980.00,
		'CZK' => 15.50,
		'DKK' => 4.70,
		'HKD' => 5.50,
		'HUF' => 252.00,
		'ILS' => 2.50,
		'INR' => 60.00,
		'JPY' => 100,
		'KRW' => 980,
		'MXN' => 13.70,
		'NOK' => 7.25,
		'NZD' => 1.20,
		'PLN' => 2.65,
		'RUB' => 59.00,
		'SEK' => 6.80,
		'SGD' => 0.90,
		'THB' => 23.00,
		'TRY' => 27.00,
		'TWD' => 25.00,
		'UAH' => 29.00,
		'VND' => 20276,
		'ZAR' => 12.75,
	];

	/**
	 * @var ?GatewayPaddle
	 */
	protected ?GatewayPaddle $gateway;

	protected static ?PaddleService $instance = null;

	public static function init( $gateway = null ): PaddleService {
		if ( null === self::$instance ) {
			self::$instance = new self( $gateway );
		}

		return self::$instance;
	}

	public function __construct( $gateway = null ) {
		if ( $gateway instanceof GatewayPaddle ) {
			$this->gateway = $gateway;
		} else {
			$this->gateway = Payment_Gateways::get_instance()->get_gateway( 'paddle' );
		}

		$this->init_settings();
	}

	public function init_settings() {
		if ( ! $this->gateway ) {
			return;
		}

		if ( ! $this->gateway->is_enabled || 'paddle' !== $this->gateway->id ) {
			return;
		}

		$this->is_live      = $this->gateway->get_option( 'is_production', true );
		$key_type           = $this->is_live ? '' : 'test_';
		$this->client_token = $this->gateway->get_option( $key_type . 'client_token' );
		$this->api_key      = $this->gateway->get_option( $key_type . 'api_key' );
		$this->currency     = Formatting::get_currency();
	}

	/**
	 * List of currencies supported by Paddle
	 *
	 * @return string[]
	 */
	public static function get_supported_currencies(): array {
		return self::$supported_currencies;
	}

	/**
	 * List of currencies supported by Paddle that has no decimals
	 *
	 * @return string[]
	 */
	public static function no_decimal_currencies(): array {
		return self::$zero_decimal_currencies;
	}

	/**
	 * @param string $api_key
	 * @param bool $is_live
	 *
	 * @return bool
	 */
	public static function validate_api_key( string $api_key, bool $is_live = false ): bool {
		$base_url = $is_live ? 'https://api.paddle.com/' : 'https://sandbox-api.paddle.com/';

		$response = wp_remote_get( $base_url . 'event-types', [
			'headers' => [
				'content-type'  => 'application/json',
				'Authorization' => 'Bearer ' . $api_key,
			],
			'timeout' => 15,
		] );

		if ( is_wp_error( $response ) ) {
			return false;
		}

		$code = wp_remote_retrieve_response_code( $response );

		return 200 === $code;
	}

	/**
	 * Get Paddle amount to pay.
	 * Amount is in cents, for some countries it needs to be multiplied by 1000.
	 *
	 * @param float|int $total Amount due.
	 * @param string $currency Accepted currency.
	 *
	 * @return float|int
	 */
	public static function get_paddle_amount( $total, string $currency = '' ) {
		if ( ! $currency ) {
			$currency = Formatting::get_currency();
		}

		if ( in_array( $currency, self::no_decimal_currencies(), true ) ) {
			return absint( round( $total ) );
		}

		return absint( Formatting::format_decimal( ( (float) $total * 100 ), Formatting::get_price_decimals() ) ); // In cents.
	}

	public function get_api_url( string $endpoint ): string {
		$base_url = $this->is_live ? 'https://api.paddle.com/' : 'https://sandbox-api.paddle.com/';

		return $base_url . $endpoint;
	}

	public function request( string $endpoint, string $method = 'GET', array $data = [] ) {
		return wp_remote_request( $this->get_api_url( $endpoint ), [
			'method'  => $method,
			'headers' => [
				'content-type'  => 'application/json',
				'Authorization' => 'Bearer ' . $this->api_key,
			],
			'body'    => ! empty( $data ) ? wp_json_encode( $data ) : [],
			'timeout' => 15,
		] );
	}

	/**
	 * Checks Stripe minimum order value authorized per currency
	 */
	public static function get_minimum_amount( $currency = '' ) {
		if ( ! $currency ) {
			$currency = Formatting::get_currency();
		}

		return self::$currency_minimum_charges[ $currency ] ?? .50;
	}

	public function create_transaction( int $order_id, array $items, $discount_id = null ) {
		$response = $this->request( 'transactions', 'POST', [
			'items'       => $items,
			'discount_id' => $discount_id,
			'custom_data' => [
				'merchant_order_id' => $order_id,
			],
		] );

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

		$status_code = wp_remote_retrieve_response_code( $response );
		$body        = wp_remote_retrieve_body( $response );
		if ( ! empty( $body ) && ! is_wp_error( $body ) ) {
			$body = json_decode( $body );
		}

		if ( 201 !== $status_code || isset( $body->error ) ) {
			return new WP_Error(
				'paddle_transaction_create_error',
				sprintf(
				// translators: %s. Paddle error message.
					__( '%s.<br/> Please contact the site administrator.', 'storeengine' ),
					$body->error->detail
				),
				$body
			);
		}

		return $body;
	}

	public function update_transaction( string $transaction_id, array $data ) {
		$response = $this->request( 'transactions/' . $transaction_id, 'PATCH', $data );
		if ( is_wp_error( $response ) ) {
			return $response;
		}

		$status_code = wp_remote_retrieve_response_code( $response );
		$body        = wp_remote_retrieve_body( $response );
		if ( ! empty( $body ) && ! is_wp_error( $body ) ) {
			$body = json_decode( $body );
		}
		if ( 200 !== $status_code ) {
			return new WP_Error( 'paddle_transaction_update_error', ucfirst( $body->error->detail ) . '.<br/> Please contact the site administrator.' );
		}

		return $body;
	}

	public function get_transaction( string $transaction_id ) {
		$response = $this->request( 'transactions/' . $transaction_id );
		if ( is_wp_error( $response ) ) {
			return $response;
		}

		$status_code = wp_remote_retrieve_response_code( $response );
		if ( 200 !== $status_code ) {
			return new WP_Error( 'paddle_invalid_transaction', 'Invalid paddle transaction Id.' );
		}

		$body = wp_remote_retrieve_body( $response );
		$body = json_decode( $body );

		if ( ! $body || ! isset( $body->data->id ) ) {
			return new WP_Error( 'paddle_get_transaction_error', 'Failed to get paddle transaction.' );
		}

		return $body->data;
	}

	public function create_discount( int $order_id, array $coupons ) {
		if ( empty( $coupons ) ) {
			return new WP_Error( 'paddle_empty_coupons', 'Empty coupons on checkout.' );
		}

		$amount = 0;
		foreach ( $coupons as $coupon_amount ) {
			$amount += $coupon_amount;
		}

		$response = $this->request( 'discounts', 'POST', [
			'amount'        => (string) $this->get_paddle_amount( $amount ),
			'description'   => "Discount for order #$order_id",
			'type'          => 'flat',
			'mode'          => 'custom',
			'usage_limit'   => 1,
			'currency_code' => $this->currency,
			'custom_data'   => [
				'merchant_order_id' => $order_id,
				'coupons'           => $coupons,
			],
		] );

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

		$status_code = wp_remote_retrieve_response_code( $response );
		$body        = wp_remote_retrieve_body( $response );
		if ( ! empty( $body ) ) {
			$body = json_decode( $body );
		}

		if ( 201 !== $status_code ) {
			return new WP_Error( 'paddle_discount_create_error', ucfirst( $body->error->detail ) . '.<br/> Please contact the site administrator.' );
		}

		return $body;
	}
}
