<?php
/**
 * Square API wrapper.
 *
 * @package SHC4WC
 */

// phpcs:disable Squiz.Commenting
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class SHC4WC_API {
	private $settings;

	public function __construct( $settings ) {
		$this->settings = is_array( $settings ) ? $settings : array();
	}

	public function get_environment() {
		return ( isset( $this->settings['environment'] ) && 'live' === $this->settings['environment'] ) ? 'live' : 'sandbox';
	}

	public function get_api_base() {
		return ( 'live' === $this->get_environment() ) ? 'https://connect.squareup.com' : 'https://connect.squareupsandbox.com';
	}

	public function get_access_token() {
		$oauth = get_option( 'shc4wc_oauth_data', array() );
		return isset( $oauth['access_token'] ) ? sanitize_text_field( $oauth['access_token'] ) : '';
	}

	public function log( $message, $context = array(), $level = 'info' ) {
		if ( empty( $this->settings['debug'] ) || 'yes' !== $this->settings['debug'] ) {
			return;
		}
		$line = $message . ' ' . wp_json_encode( $context );
		if ( function_exists( 'wc_get_logger' ) ) {
			wc_get_logger()->log( $level, $line, array( 'source' => 'shc4wc' ) );
			return;
		}
		// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
		error_log( '[shc4wc] ' . $line );
	}

	public function request( $method, $path, $body = null, $extra_headers = array() ) {
		$url     = trailingslashit( $this->get_api_base() ) . ltrim( $path, '/' );
		$headers = array_merge(
			array(
				'Authorization'  => 'Bearer ' . $this->get_access_token(),
				'Content-Type'   => 'application/json',
				'Square-Version' => '2024-12-18',
			),
			$extra_headers
		);

		$args = array(
			'method'  => strtoupper( $method ),
			'headers' => $headers,
			'timeout' => 60,
		);

		if ( null !== $body ) {
			$args['body'] = wp_json_encode( $body );
		}

		$this->log(
			'Square API request',
			array(
				'url'    => $url,
				'method' => $method,
				'body'   => $body,
			)
		);
		$response = wp_remote_request( $url, $args );

		if ( is_wp_error( $response ) ) {
			$this->log( 'Square API wp_error', array( 'error' => $response->get_error_message() ), 'error' );
			return $response;
		}

		$code = (int) wp_remote_retrieve_response_code( $response );
		$raw  = wp_remote_retrieve_body( $response );
		$data = json_decode( $raw, true );
		if ( ! is_array( $data ) ) {
			$data = array();
		}

		$this->log(
			'Square API response',
			array(
				'code' => $code,
				'body' => $data,
			)
		);
		if ( $code < 200 || $code >= 300 ) {
			return new WP_Error(
				'shc4wc_square_api_error',
				'Square API request failed',
				array(
					'status' => $code,
					'data'   => $data,
				)
			);
		}

		return $data;
	}

	public function list_locations( $access_token = '' ) {
		$token = $access_token ? $access_token : $this->get_access_token();
		if ( empty( $token ) ) {
			return new WP_Error( 'shc4wc_no_access_token', 'Square access token missing.' );
		}

		$url = trailingslashit( $this->get_api_base() ) . 'v2/locations';
		$this->log( 'Square locations request', array( 'url' => $url ) );

		$response = wp_remote_get(
			$url,
			array(
				'headers' => array(
					'Authorization'  => 'Bearer ' . $token,
					'Content-Type'   => 'application/json',
					'Square-Version' => '2024-12-18',
				),
				'timeout' => 60,
			)
		);

		if ( is_wp_error( $response ) ) {
			$this->log( 'Square locations wp_error', array( 'error' => $response->get_error_message() ), 'error' );
			return $response;
		}

		$code = (int) wp_remote_retrieve_response_code( $response );
		$raw  = wp_remote_retrieve_body( $response );
		$data = json_decode( $raw, true );
		if ( ! is_array( $data ) ) {
			$data = array();
		}

		if ( $code < 200 || $code >= 300 ) {
			$this->log(
				'Square locations failed',
				array(
					'status' => $code,
					'body'   => $data,
				),
				'error'
			);
			return new WP_Error(
				'shc4wc_square_locations_error',
				'Square locations request failed',
				array(
					'status' => $code,
					'data'   => $data,
				)
			);
		}

		return $data;
	}

	/**
	 * Create a Square Hosted Checkout payment link.
	 *
	 * @param float  $amount      Major currency units (e.g. 25.00).
	 * @param string $currency    ISO currency code.
	 * @param string $reference   Unique reference id stored on the Square order.
	 * @param array  $args        Optional description, return_args, line_name, location_id.
	 * @return array|WP_Error
	 */
	public function create_payment_link( $amount, $currency, $reference, $args = array() ) {
		$settings    = $this->settings;
		$location_id = isset( $args['location_id'] ) ? trim( (string) $args['location_id'] ) : '';
		if ( '' === $location_id && ! empty( $settings['location_id'] ) ) {
			$location_id = trim( (string) $settings['location_id'] );
		}
		if ( '' === $location_id ) {
			return new WP_Error( 'shc4wc_no_location', 'Square location id missing.' );
		}

		$amount_minor = (int) round( (float) $amount * 100 );
		if ( $amount_minor <= 0 ) {
			return new WP_Error( 'shc4wc_invalid_amount', 'Payment amount must be greater than zero.' );
		}

		$currency    = strtoupper( sanitize_text_field( (string) $currency ) );
		$reference   = sanitize_text_field( (string) $reference );
		$description = isset( $args['description'] ) ? (string) $args['description'] : $reference;
		$line_name   = isset( $args['line_name'] ) ? (string) $args['line_name'] : $description;
		$return_args = isset( $args['return_args'] ) && is_array( $args['return_args'] ) ? $args['return_args'] : array();

		$redirect_query = array_merge(
			array( 'wc-api' => 'shc4wc_return' ),
			$return_args
		);

		$payload = array(
			'idempotency_key'  => wp_generate_uuid4(),
			'description'      => $description,
			'order'            => array(
				'location_id'  => $location_id,
				'reference_id' => $reference,
				'line_items'   => array(
					array(
						'name'             => $line_name,
						'quantity'         => '1',
						'base_price_money' => array(
							'amount'   => $amount_minor,
							'currency' => $currency,
						),
					),
				),
			),
			'checkout_options' => array(
				'redirect_url' => add_query_arg( $redirect_query, home_url( '/' ) ),
			),
			'payment_note'     => $description,
		);

		return $this->request( 'POST', '/v2/online-checkout/payment-links', $payload );
	}

	/**
	 * Create a Square hosted checkout link for a subscription plan variation.
	 *
	 * @see https://developer.squareup.com/docs/checkout-api/subscription-plan-checkout
	 *
	 * @param float  $amount              Initial phase price (major units).
	 * @param string $currency            ISO currency code.
	 * @param string $reference           Unique reference id.
	 * @param string $plan_variation_id   Square subscription plan *variation* id.
	 * @param array  $args                description, line_name, location_id, return_args.
	 * @return array|WP_Error
	 */
	public function create_subscription_payment_link( $amount, $currency, $reference, $plan_variation_id, $args = array() ) {
		$settings    = $this->settings;
		$location_id = isset( $args['location_id'] ) ? trim( (string) $args['location_id'] ) : '';
		if ( '' === $location_id && ! empty( $settings['location_id'] ) ) {
			$location_id = trim( (string) $settings['location_id'] );
		}
		if ( '' === $location_id ) {
			return new WP_Error( 'shc4wc_no_location', 'Square location id missing.' );
		}

		$plan_variation_id = sanitize_text_field( (string) $plan_variation_id );
		if ( '' === $plan_variation_id ) {
			return new WP_Error( 'shc4wc_no_subscription_plan', 'Square subscription plan variation id missing.' );
		}

		$amount_minor = (int) round( (float) $amount * 100 );
		if ( $amount_minor <= 0 ) {
			return new WP_Error( 'shc4wc_invalid_amount', 'Subscription amount must be greater than zero.' );
		}

		$currency    = strtoupper( sanitize_text_field( (string) $currency ) );
		$reference   = sanitize_text_field( (string) $reference );
		$description = isset( $args['description'] ) ? (string) $args['description'] : $reference;
		$line_name   = isset( $args['line_name'] ) ? (string) $args['line_name'] : $description;
		$return_args = isset( $args['return_args'] ) && is_array( $args['return_args'] ) ? $args['return_args'] : array();

		$redirect_query = array_merge(
			array( 'wc-api' => 'shc4wc_return' ),
			$return_args
		);

		$payload = array(
			'idempotency_key'  => wp_generate_uuid4(),
			'description'      => $description,
			'quick_pay'        => array(
				'location_id' => $location_id,
				'name'        => $line_name,
				'price_money' => array(
					'amount'   => $amount_minor,
					'currency' => $currency,
				),
			),
			'checkout_options' => array(
				'redirect_url'         => add_query_arg( $redirect_query, home_url( '/' ) ),
				'subscription_plan_id' => $plan_variation_id,
			),
		);

		return $this->request( 'POST', '/v2/online-checkout/payment-links', $payload );
	}

	/**
	 * Search Square catalog subscription plan variations.
	 *
	 * @param string $cursor Pagination cursor.
	 * @return array|WP_Error
	 */
	public function search_subscription_plan_variations( $cursor = '' ) {
		$body = array(
			'object_types'            => array( 'SUBSCRIPTION_PLAN_VARIATION' ),
			'include_related_objects' => true,
		);
		if ( '' !== (string) $cursor ) {
			$body['cursor'] = (string) $cursor;
		}

		return $this->request( 'POST', '/v2/catalog/search', $body );
	}

	public function create_payment_link_for_order( WC_Order $order, $location_id ) {
		return $this->create_payment_link(
			(float) $order->get_total(),
			$order->get_currency(),
			'wc_order_' . $order->get_id(),
			array(
				'location_id' => $location_id,
				'description'   => 'WooCommerce Order #' . $order->get_id(),
				'line_name'     => 'Order #' . $order->get_order_number(),
				'return_args'   => array(
					'order_id'  => $order->get_id(),
					'order_key' => $order->get_order_key(),
				),
			)
		);
	}

	/**
	 * Load a Square Order by id (used after buyer return to read tenders → payment id without webhooks).
	 *
	 * @param string $square_order_id Square Order id from payment-link response.
	 * @return array|WP_Error
	 */
	public function retrieve_order( $square_order_id ) {
		$square_order_id = sanitize_text_field( (string) $square_order_id );
		if ( '' === $square_order_id ) {
			return new WP_Error( 'shc4wc_no_square_order', 'Square order id missing.' );
		}
		return $this->request( 'GET', '/v2/orders/' . rawurlencode( $square_order_id ) );
	}

	/**
	 * Retrieve an Online Checkout payment link (after buyer return, related orders may include tenders sooner than GET /v2/orders alone).
	 *
	 * @param string $payment_link_id Payment link id from create response.
	 * @return array|WP_Error
	 */
	public function retrieve_payment_link( $payment_link_id ) {
		$payment_link_id = sanitize_text_field( (string) $payment_link_id );
		if ( '' === $payment_link_id ) {
			return new WP_Error( 'shc4wc_no_payment_link', 'Square payment link id missing.' );
		}
		return $this->request( 'GET', '/v2/online-checkout/payment-links/' . rawurlencode( $payment_link_id ) );
	}

	/**
	 * List payments for a location (GET /v2/payments). Square does not expose POST /v2/payments/search on Connect; match client-side by order_id.
	 *
	 * @param string $location_id Location id.
	 * @param string $begin_time_rfc3339 Inclusive lower bound (UTC RFC3339).
	 * @param string $cursor Pagination cursor from a previous response, or empty.
	 * @return array|WP_Error
	 */
	public function list_recent_payments( $location_id, $begin_time_rfc3339, $cursor = '' ) {
		$location_id = sanitize_text_field( (string) $location_id );
		if ( '' === $location_id ) {
			return new WP_Error( 'shc4wc_no_location', 'Square location id missing.' );
		}
		$args = array(
			'location_id' => $location_id,
			'begin_time'  => $begin_time_rfc3339,
			'limit'       => 100,
			'sort_order'  => 'DESC',
			'sort_field'  => 'CREATED_AT',
		);
		if ( '' !== (string) $cursor ) {
			$args['cursor'] = (string) $cursor;
		}
		$url = add_query_arg(
			$args,
			trailingslashit( $this->get_api_base() ) . 'v2/payments'
		);

		$response = wp_remote_get(
			$url,
			array(
				'headers' => array(
					'Authorization'  => 'Bearer ' . $this->get_access_token(),
					'Content-Type'   => 'application/json',
					'Square-Version' => '2024-12-18',
				),
				'timeout' => 60,
			)
		);

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

		$code = (int) wp_remote_retrieve_response_code( $response );
		$raw  = wp_remote_retrieve_body( $response );
		$data = json_decode( $raw, true );
		if ( ! is_array( $data ) ) {
			$data = array();
		}
		if ( $code < 200 || $code >= 300 ) {
			return new WP_Error(
				'shc4wc_square_list_payments_error',
				'Square list payments failed',
				array(
					'status' => $code,
					'data'   => $data,
				)
			);
		}

		return $data;
	}

	/**
	 * Retrieve Square Checkout merchant settings.
	 *
	 * @return array|WP_Error
	 */
	public function retrieve_merchant_checkout_settings() {
		return $this->request( 'GET', '/v2/online-checkout/merchant-settings' );
	}

	/**
	 * Update Square Checkout merchant settings.
	 *
	 * @param array $merchant_settings Partial merchant_settings payload.
	 * @return array|WP_Error
	 */
	public function update_merchant_checkout_settings( array $merchant_settings ) {
		return $this->request(
			'PUT',
			'/v2/online-checkout/merchant-settings',
			array(
				'merchant_settings' => $merchant_settings,
			)
		);
	}

	/**
	 * Retrieve Square Checkout location settings.
	 *
	 * @param string $location_id Square location id.
	 * @return array|WP_Error
	 */
	public function retrieve_location_checkout_settings( $location_id ) {
		$location_id = sanitize_text_field( (string) $location_id );
		if ( '' === $location_id ) {
			return new WP_Error( 'shc4wc_no_location', 'Square location id missing.' );
		}

		return $this->request( 'GET', '/v2/online-checkout/location-settings/' . rawurlencode( $location_id ) );
	}

	/**
	 * Update Square Checkout location settings.
	 *
	 * @param string $location_id       Square location id.
	 * @param array  $location_settings Partial location_settings payload.
	 * @return array|WP_Error
	 */
	public function update_location_checkout_settings( $location_id, array $location_settings ) {
		$location_id = sanitize_text_field( (string) $location_id );
		if ( '' === $location_id ) {
			return new WP_Error( 'shc4wc_no_location', 'Square location id missing.' );
		}

		return $this->request(
			'PUT',
			'/v2/online-checkout/location-settings/' . rawurlencode( $location_id ),
			array(
				'location_settings' => $location_settings,
			)
		);
	}
}
