<?php
/**
 * Main payment gateway class.
 *
 * @package SHC4WC
 */

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

if ( ! class_exists( 'WC_Payment_Gateway' ) ) {
	return;
}

class SHC4WC_Gateway extends WC_Payment_Gateway {
	public function __construct() {
		$this->id                 = 'shc4wc';
		$this->method_title       = __( 'Payments Square Connect', 'payments-connect-square' );
		$this->method_description = __( 'Connect WooCommerce checkout to Square and redirect customers to Square secure hosted checkout.', 'payments-connect-square' );
		$this->has_fields         = false;
		$this->supports           = array( 'products', 'refunds' );
		if ( class_exists( 'WC_Subscriptions' ) || function_exists( 'wcs_is_subscription' ) ) {
			$this->supports[] = 'subscriptions';
		}

		$this->init_form_fields();
		$this->init_settings();

		$this->title       = $this->get_option( 'title' );
		$this->description = self::format_checkout_description(
			$this->get_option( 'description' ),
			$this->get_shared_environment()
		);
		$this->enabled     = $this->get_option( 'enabled' );

		add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
		add_action( 'admin_notices', array( $this, 'admin_notice' ) );
	}

	public function init_form_fields() {
		$connection_url = class_exists( 'SHC4WC_Connection_Settings' )
			? SHC4WC_Connection_Settings::page_url()
			: SHC4WC_Admin_Menu::page_url();

		$this->form_fields = array(
			'enabled' => array(
				'title'   => __( 'Enable/Disable', 'payments-connect-square' ),
				'type'    => 'checkbox',
				'label'   => __( 'Enable Payments Square Connect', 'payments-connect-square' ),
				'default' => 'no',
			),
			'title'   => array(
				'title'       => __( 'Title', 'payments-connect-square' ),
				'type'        => 'text',
				'description' => __( 'Displayed at checkout.', 'payments-connect-square' ),
				'default'     => __( 'Pay with Square', 'payments-connect-square' ),
				'desc_tip'    => true,
			),
			'description' => array(
				'title'   => __( 'Description', 'payments-connect-square' ),
				'type'    => 'textarea',
				'default' => __( 'You will be redirected to Square secure hosted checkout.', 'payments-connect-square' ),
			),
			'payment_reminders_section' => array(
				'title'       => __( 'Payment reminders', 'payments-connect-square' ),
				'type'        => 'title',
				'description' => __( 'Automatically email customers who have not paid yet. Each reminder includes the same Square Hosted Checkout payment link as Pay for Order.', 'payments-connect-square' ),
			),
			'payment_reminders' => array(
				'title'   => __( 'Automatic reminders', 'payments-connect-square' ),
				'type'    => 'checkbox',
				'label'   => __( 'Send payment reminder emails for unpaid orders', 'payments-connect-square' ),
				'default' => 'no',
			),
			'payment_reminder_hours_1' => array(
				'title'       => __( 'First reminder (hours)', 'payments-connect-square' ),
				'type'        => 'number',
				'description' => __( 'Hours after the order was placed (or after the last reminder). Default: 24.', 'payments-connect-square' ),
				'default'     => '24',
				'custom_attributes' => array(
					'min'  => '1',
					'step' => '1',
				),
				'desc_tip'    => true,
			),
			'payment_reminder_hours_2' => array(
				'title'       => __( 'Second reminder (hours)', 'payments-connect-square' ),
				'type'        => 'number',
				'description' => __( 'Hours after the first reminder. Default: 72. Set to 0 to disable.', 'payments-connect-square' ),
				'default'     => '72',
				'custom_attributes' => array(
					'min'  => '0',
					'step' => '1',
				),
				'desc_tip'    => true,
			),
			'payment_reminder_hours_3' => array(
				'title'       => __( 'Third reminder (hours)', 'payments-connect-square' ),
				'type'        => 'number',
				'description' => __( 'Hours after the second reminder. Default: 168 (7 days). Set to 0 to disable.', 'payments-connect-square' ),
				'default'     => '168',
				'custom_attributes' => array(
					'min'  => '0',
					'step' => '1',
				),
				'desc_tip'    => true,
			),
			'connection_note' => array(
				'title'       => __( 'Square connection', 'payments-connect-square' ),
				'type'        => 'title',
				'description' => sprintf(
					/* translators: %s: settings page URL. */
					__( 'OAuth, location, and debug settings are managed under <a href="%s">Payments Square Connect</a>.', 'payments-connect-square' ),
					esc_url( $connection_url )
				),
			),
		);
	}

	public function admin_notice() {
		static $shown_statuses = array();

		$page    = (string) filter_input( INPUT_GET, 'page', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
		$tab     = (string) filter_input( INPUT_GET, 'tab', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
		$section = (string) filter_input( INPUT_GET, 'section', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
		$status  = (string) filter_input( INPUT_GET, 'shc4wc_status', FILTER_SANITIZE_FULL_SPECIAL_CHARS );

		if ( '' === $status ) {
			return;
		}

		$on_connection_page = class_exists( 'SHC4WC_Connection_Settings' ) && SHC4WC_Connection_Settings::page_slug() === $page;
		$on_gateway_page    = 'wc-settings' === $page && 'checkout' === $tab && 'shc4wc' === $section;
		if ( ! $on_connection_page && ! $on_gateway_page ) {
			return;
		}
		if ( isset( $shown_statuses[ $status ] ) ) {
			return;
		}

		$map = array(
			'connected'                       => array( 'success', __( 'Square connected successfully.', 'payments-connect-square' ) ),
			'disconnected'                    => array( 'success', __( 'Square disconnected.', 'payments-connect-square' ) ),
			'missing_client_id'               => array( 'error', __( 'Client ID not found in credentials JSON for selected environment.', 'payments-connect-square' ) ),
			'oauth_denied'                    => array( 'error', __( 'Square authorization denied by user.', 'payments-connect-square' ) ),
			'oauth_state_error'               => array( 'error', __( 'OAuth state validation failed.', 'payments-connect-square' ) ),
			'oauth_request_failed'            => array( 'error', __( 'Could not connect to Square token endpoint.', 'payments-connect-square' ) ),
			'oauth_exchange_failed'           => array( 'error', __( 'Square token exchange failed.', 'payments-connect-square' ) ),
			'oauth_locations_failed'          => array( 'error', __( 'Access token received, but locations could not be fetched. Check permissions and retry.', 'payments-connect-square' ) ),
			'oauth_worker_misconfigured'      => array( 'error', __( 'Square login could not start. Register this store, save a connection key, and try again - or contact support.', 'payments-connect-square' ) ),
			'worker_register_invalid_base'    => array( 'error', __( 'Worker base URL must start with https://.', 'payments-connect-square' ) ),
			'worker_register_failed'          => array( 'error', __( 'Could not register this site with the OAuth service. Check WooCommerce -> Status -> Logs (source: shc4wc) for details.', 'payments-connect-square' ) ),
			'worker_register_secret_required' => array(
				'error',
				__(
					'The OAuth Worker requires a registration secret. Set oauth_worker_registration_secret in shc4wc-host-config.php to the same value as Cloudflare secret REGISTRATION_SECRET - or remove REGISTRATION_SECRET from the Worker for open registration.',
					'payments-connect-square'
				),
			),
			'worker_register_unreachable'     => array(
				'error',
				__( 'Could not reach the OAuth Worker (network error). Check the Worker URL, firewall, and PHP SSL. See WooCommerce logs (shc4wc).', 'payments-connect-square' ),
			),
			'worker_register_bad_home_url'    => array(
				'error',
				__( 'The Worker rejected your site URL. In Settings -> General, set WordPress Address and Site Address to a full URL starting with http:// or https://.', 'payments-connect-square' ),
			),
			'worker_registered'               => array( 'success', __( 'Store registered. You can connect Payments Square Connect.', 'payments-connect-square' ) ),
			'oauth_claim_failed'              => array( 'error', __( 'Could not complete OAuth with the Worker (claim failed or session expired). Try connecting again.', 'payments-connect-square' ) ),
		);

		if ( ! isset( $map[ $status ] ) ) {
			return;
		}

		$shown_statuses[ $status ] = true;
		list( $type, $message )    = $map[ $status ];
		printf( '<div class="notice notice-%1$s"><p>%2$s</p></div>', esc_attr( $type ), esc_html( $message ) );
	}

	public function generate_shc4wc_oauth_actions_html( $key, $data ) {
		$title = isset( $data['title'] ) ? $data['title'] : __( 'OAuth', 'payments-connect-square' );
		return SHC4WC_Connection_Settings::render_oauth_actions_row( $title );
	}

	public function generate_shc4wc_worker_register_html( $key, $data ) {
		$field_key = $this->get_field_key( $key );
		$title     = isset( $data['title'] ) ? $data['title'] : __( 'Worker registration', 'payments-connect-square' );
		$register  = wp_nonce_url( admin_url( 'admin-post.php?action=shc4wc_worker_register' ), 'shc4wc_worker_register' );
		$merch     = SHC4WC_Plugin::is_merchant_connect_ui();

		$html  = '<tr valign="top">';
		$html .= '<th scope="row" class="titledesc"><label for="' . esc_attr( $field_key ) . '">' . esc_html( $title ) . '</label></th>';
		$html .= '<td class="forminp">';
		$html .= '<a class="button button-secondary" href="' . esc_url( $register ) . '">' . esc_html(
			$merch
				? __( 'Register this store', 'payments-connect-square' )
				: __( 'Register site with Worker (save settings first)', 'payments-connect-square' )
		) . '</a>';
		$html .= '<p class="description">' . esc_html(
			$merch
				? __( 'Save your settings at the bottom of this page first if you changed them. This links this site to your payment setup.', 'payments-connect-square' )
				: __( 'Sends your site URL to the Worker and stores the returned site key in these settings.', 'payments-connect-square' )
		) . '</p>';
		$html .= '</td></tr>';

		return $html;
	}

	public function generate_shc4wc_webhook_url_html( $key, $data ) {
		$title = isset( $data['title'] ) ? $data['title'] : __( 'Webhook URL', 'payments-connect-square' );
		return SHC4WC_Connection_Settings::render_webhook_url_row( $title );
	}

	public function is_available() {
		if ( 'yes' !== $this->enabled ) {
			return false;
		}
		return SHC4WC_Plugin::is_connected();
	}

	/**
	 * Customer-facing checkout description; adds a test-mode note when not on Live.
	 *
	 * @param string $description Saved gateway description.
	 * @param string $environment sandbox|live.
	 * @return string
	 */
	public static function format_checkout_description( $description, $environment ) {
		$description = trim( (string) $description );
		if ( '' === $description ) {
			$description = __( 'You will be redirected to Square secure hosted checkout.', 'payments-connect-square' );
		}
		if ( 'live' !== $environment ) {
			$description .= ' ' . __( 'Test mode — no real payment will be taken.', 'payments-connect-square' );
		}
		return $description;
	}

	private function get_shared_environment() {
		$settings = SHC4WC_Plugin::get_settings();
		return ( isset( $settings['environment'] ) && 'live' === $settings['environment'] ) ? 'live' : 'sandbox';
	}

	private function get_location_options() {
		$locations = SHC4WC_Plugin::get_locations();
		if ( empty( $locations ) ) {
			return array(
				'' => __( 'Use Payments Square Connect to load locations', 'payments-connect-square' ),
			);
		}

		$options = array(
			'' => __( 'Select a location', 'payments-connect-square' ),
		);

		foreach ( $locations as $location ) {
			if ( empty( $location['id'] ) ) {
				continue;
			}
			$name                       = ! empty( $location['name'] ) ? $location['name'] : $location['id'];
			$options[ $location['id'] ] = sprintf( '%s (%s)', $name, $location['id'] );
		}

		return $options;
	}

	public function process_payment( $order_id ) {
		$order = wc_get_order( $order_id );
		$settings    = SHC4WC_Plugin::get_settings();
		$location_id = isset( $settings['location_id'] ) ? trim( (string) $settings['location_id'] ) : '';

		if ( ! $order || ! $location_id ) {
			wc_add_notice( __( 'Payment configuration issue. Please contact support.', 'payments-connect-square' ), 'error' );
			return array( 'result' => 'failure' );
		}

		$api      = new SHC4WC_API( $settings );
		$subscription = SHC4WC_Subscriptions::get_order_subscription_checkout( $order );
		if ( $subscription ) {
			$response = $api->create_subscription_payment_link(
				(float) $subscription['amount'],
				$order->get_currency(),
				'wc_order_' . $order->get_id(),
				$subscription['variation_id'],
				array(
					'location_id' => $location_id,
					'description' => 'WooCommerce Subscription Order #' . $order->get_id(),
					'line_name'   => $subscription['name'],
					'return_args' => array(
						'order_id'  => $order->get_id(),
						'order_key' => $order->get_order_key(),
					),
					'customer'    => array(
						'email'           => (string) $order->get_billing_email(),
						'given_name'      => (string) $order->get_billing_first_name(),
						'family_name'     => (string) $order->get_billing_last_name(),
						'phone'           => (string) $order->get_billing_phone(),
						'billing_country' => (string) $order->get_billing_country(),
						'wp_user_id'      => (int) $order->get_user_id(),
					),
				)
			);
		} else {
			$response = $api->create_payment_link_for_order( $order, $location_id );
		}
		if ( is_wp_error( $response ) ) {
			$api->log( 'Create payment link failed', array( 'error' => $response->get_error_data() ), 'error' );
			wc_add_notice( __( 'Square payment link generation failed.', 'payments-connect-square' ), 'error' );
			return array( 'result' => 'failure' );
		}

		$link = isset( $response['payment_link']['url'] ) ? esc_url_raw( $response['payment_link']['url'] ) : '';
		if ( empty( $link ) ) {
			wc_add_notice( __( 'Invalid response from Square.', 'payments-connect-square' ), 'error' );
			return array( 'result' => 'failure' );
		}

		// Square order id: API shape varies; store from any known path for buyer-return + Payments lookup.
		$square_order_id = '';
		if ( ! empty( $response['related_resources']['orders'][0]['id'] ) ) {
			$square_order_id = sanitize_text_field( (string) $response['related_resources']['orders'][0]['id'] );
		} elseif ( ! empty( $response['payment_link']['order_id'] ) ) {
			$square_order_id = sanitize_text_field( (string) $response['payment_link']['order_id'] );
		}
		if ( '' !== $square_order_id ) {
			$order->update_meta_data( '_shc4wc_square_order_id', $square_order_id );
		}
		if ( ! empty( $response['payment_link']['id'] ) ) {
			$order->update_meta_data( '_shc4wc_payment_link_id', sanitize_text_field( (string) $response['payment_link']['id'] ) );
		}
		$order->update_meta_data( '_shc4wc_square_location_id', sanitize_text_field( (string) $location_id ) );

		$order->update_meta_data( '_shc4wc_payment_link_url', $link );
		if ( $subscription ) {
			$order->update_meta_data( '_shc4wc_square_subscription_variation_id', $subscription['variation_id'] );
		}
		$order->update_status( 'pending', __( 'Awaiting payment — customer redirected to Square Hosted Checkout.', 'payments-connect-square' ) );
		$order->save();

		/**
		 * Fires before redirecting a WooCommerce customer to Square Hosted Checkout.
		 *
		 * @param WC_Order $order WooCommerce order.
		 * @param string   $link  Square payment link URL.
		 */
		do_action( 'shc4wc_checkout_redirect_to_square', $order, $link );

		return array(
			'result'   => 'success',
			'redirect' => $link,
		);
	}

	/**
	 * Refund a WooCommerce order through Square Payments API.
	 *
	 * @param int        $order_id Order ID.
	 * @param float|null $amount   Refund amount in order currency.
	 * @param string     $reason   Optional refund reason.
	 * @return true|WP_Error
	 */
	public function process_refund( $order_id, $amount = null, $reason = '' ) {
		$order = wc_get_order( $order_id );
		if ( ! $order ) {
			return new WP_Error( 'shc4wc_refund_invalid_order', __( 'Invalid order.', 'payments-connect-square' ) );
		}

		if ( null === $amount || '' === $amount ) {
			$amount = $order->get_total();
		}

		$amount = (float) wc_format_decimal( $amount, wc_get_price_decimals() );
		if ( $amount <= 0 ) {
			return new WP_Error( 'shc4wc_refund_invalid_amount', __( 'Refund amount must be greater than zero.', 'payments-connect-square' ) );
		}

		$payment_id = $this->resolve_square_payment_id( $order );
		if ( '' === $payment_id ) {
			return new WP_Error(
				'shc4wc_refund_no_payment',
				__( 'Square payment ID was not found on this order. Wait for the payment webhook or customer return from Square, then try again.', 'payments-connect-square' )
			);
		}

		$settings = SHC4WC_Plugin::get_settings();
		$api      = new SHC4WC_API( $settings );
		$response = $api->create_refund(
			$payment_id,
			$amount,
			$order->get_currency(),
			'wc_refund_' . $order_id . '_' . wp_generate_password( 12, false, false )
		);

		if ( is_wp_error( $response ) ) {
			$api->log( 'Square refund failed', array( 'order_id' => $order_id, 'error' => $response->get_error_data() ), 'error' );
			return new WP_Error(
				'shc4wc_refund_failed',
				$this->format_square_refund_error( $response )
			);
		}

		$refund    = isset( $response['refund'] ) && is_array( $response['refund'] ) ? $response['refund'] : array();
		$status    = isset( $refund['status'] ) ? strtoupper( (string) $refund['status'] ) : '';
		$refund_id = isset( $refund['id'] ) ? sanitize_text_field( (string) $refund['id'] ) : '';

		if ( in_array( $status, array( 'FAILED', 'REJECTED' ), true ) ) {
			return new WP_Error(
				'shc4wc_refund_rejected',
				sprintf(
					/* translators: %s: Square refund status. */
					__( 'Square rejected the refund (status: %s).', 'payments-connect-square' ),
					$status
				)
			);
		}

		$note = sprintf(
			/* translators: 1: refund amount, 2: Square refund id, 3: Square refund status. */
			__( 'Square refund of %1$s issued (refund ID: %2$s, status: %3$s).', 'payments-connect-square' ),
			wc_price( $amount, array( 'currency' => $order->get_currency() ) ),
			'' !== $refund_id ? $refund_id : __( 'n/a', 'payments-connect-square' ),
			'' !== $status ? $status : __( 'unknown', 'payments-connect-square' )
		);
		if ( '' !== trim( (string) $reason ) ) {
			$note .= ' ' . sprintf(
				/* translators: %s: refund reason entered in WooCommerce. */
				__( 'Reason: %s', 'payments-connect-square' ),
				sanitize_text_field( (string) $reason )
			);
		}
		$order->add_order_note( $note );

		if ( '' !== $refund_id ) {
			$stored = $order->get_meta( '_shc4wc_square_refund_ids' );
			$ids    = is_array( $stored ) ? $stored : array();
			$ids[]  = $refund_id;
			$order->update_meta_data( '_shc4wc_square_refund_ids', array_values( array_unique( $ids ) ) );
			$order->save();
		}

		return true;
	}

	/**
	 * @param WC_Order $order WooCommerce order.
	 * @return string Square payment id or empty string.
	 */
	private function resolve_square_payment_id( WC_Order $order ) {
		$payment_id = (string) $order->get_meta( '_shc4wc_square_payment_id' );
		if ( '' !== $payment_id ) {
			return $payment_id;
		}

		$transaction_id = (string) $order->get_transaction_id();
		if ( '' !== $transaction_id ) {
			return $transaction_id;
		}

		if ( class_exists( 'SHC4WC_Plugin' ) ) {
			$plugin = SHC4WC_Plugin::instance();
			if ( method_exists( $plugin, 'retrieve_square_payment_id_for_order' ) ) {
				return (string) $plugin->retrieve_square_payment_id_for_order( $order );
			}
		}

		return '';
	}

	/**
	 * @param WP_Error $error Square API error.
	 * @return string
	 */
	private function format_square_refund_error( WP_Error $error ) {
		$data = $error->get_error_data();
		if ( is_array( $data ) && ! empty( $data['data']['errors'] ) && is_array( $data['data']['errors'] ) ) {
			$first = $data['data']['errors'][0];
			if ( ! empty( $first['detail'] ) ) {
				return sprintf(
					/* translators: %s: Square API error detail. */
					__( 'Square refund failed: %s', 'payments-connect-square' ),
					sanitize_text_field( (string) $first['detail'] )
				);
			}
			if ( ! empty( $first['code'] ) ) {
				return sprintf(
					/* translators: %s: Square API error code. */
					__( 'Square refund failed (code: %s).', 'payments-connect-square' ),
					sanitize_text_field( (string) $first['code'] )
				);
			}
		}

		return __( 'Square refund failed. Check WooCommerce logs (source: shc4wc) for details.', 'payments-connect-square' );
	}
}
