<?php

namespace DigitalApps\PaymentsForAdyen\Api;

defined('ABSPATH') || exit;

use WP_REST_Controller;
use WP_REST_Server;
use DigitalApps\PaymentsForAdyen\Adyen_Client;
use DigitalApps\PaymentsForAdyen\Logger;
use DigitalApps\PaymentsForAdyen\Settings_Store;
use DigitalApps\PaymentsForAdyen\Adyen_Endpoint_Resolver;

/**
 * REST API Controller for Adyen plugin settings
 */
class Rest_Controller extends WP_REST_Controller
{
	/**
	 * Namespace/version for REST routes
	 *
	 * @var string
	 */
	protected $namespace = 'digitalapps-payments-for-adyen/v1';

	/**
	 * Base route for settings
	 *
	 * @var string
	 */
	protected $rest_base = 'settings';

	/**
	 * Register REST API routes.
	 */
	public function register_routes()
	{
		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base,
			[
				[
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => [$this, 'get_settings'],
					'permission_callback' => [$this, 'permissions_check'],
				],
				[
					'methods'             => WP_REST_Server::EDITABLE,
					'callback'            => [$this, 'update_settings'],
					'permission_callback' => [$this, 'permissions_check'],
					'args'                => $this->get_endpoint_args_for_item_schema(false),
				],
			]
		);

		register_rest_route(
			$this->namespace,
			'/connection-test',
			[
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => [$this, 'test_connection'],
				'permission_callback' => [$this, 'permissions_check'],
			]
		);

		// Add capture payment endpoint
		register_rest_route(
			$this->namespace,
			'/capture-payment',
			[
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => [$this, 'capture_payment'],
				'permission_callback' => [$this, 'permissions_check'], // Only allow WooCommerce managers.
				'args' => [
					'order_id' => [
						'required' => true,
						'type'     => 'integer',
						'description' => __('The ID of the order to capture.', 'digital-apps-payments-for-adyen'),
					],
				],
			]
		);

		// Logs endpoints
		register_rest_route(
			$this->namespace,
			'/logs',
			[
				[
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => [$this, 'get_logs'],
					'permission_callback' => [$this, 'permissions_check'],
				],
				[
					'methods'             => WP_REST_Server::DELETABLE,
					'callback'            => [$this, 'clear_logs'],
					'permission_callback' => [$this, 'permissions_check'],
				],
			]
		);
	}

	/**
	 * Permissions check for settings and logs endpoints.
	 *
	 * @param \WP_REST_Request $request
	 * @return bool|\WP_Error
	 */
	public function permissions_check($request)
	{
		// Verify WP REST nonce
		$nonce = $request->get_header('x_wp_nonce');
		if (! $nonce || ! wp_verify_nonce($nonce, 'wp_rest')) {
			return new \WP_Error('invalid_nonce', __('Invalid or missing WP nonce', 'digital-apps-payments-for-adyen'), ['status' => 403]);
		}
		// Capability check
		if (! current_user_can('manage_woocommerce')) {
			return new \WP_Error('rest_forbidden', __('You do not have permissions to access this endpoint', 'digital-apps-payments-for-adyen'), ['status' => 403]);
		}
		return true;
	}

	/**
	 * GET /settings
	 *
	 * @param \WP_REST_Request $request
	 * @return \WP_REST_Response
	 */
	public function get_settings($request)
	{
		unset($request);
		return rest_ensure_response($this->get_public_settings(Settings_Store::get()));
	}

	/**
	 * Build the non-secret settings payload used by the React admin.
	 *
	 * @param array $options Decrypted plugin settings.
	 * @return array
	 */
	private function get_public_settings(array $options)
	{
		$test_mode = !empty($options['testMode']);
		$current_profile = Settings_Store::get_environment($test_mode, $options);
		$test_profile = Settings_Store::get_environment(true, $options);
		$live_profile = Settings_Store::get_environment(false, $options);
		$merchant_account = sanitize_text_field($current_profile['merchantAccount'] ?? '');
		$client_key = sanitize_text_field($current_profile['clientKey'] ?? '');
		$merchant_valid = $this->is_valid_merchant_account($merchant_account);
		$client_key_valid = $this->is_valid_client_key($client_key, $test_mode);
		$gateway_settings = get_option('woocommerce_digitalapps_payments_for_adyen_card_settings', []);
		$gateway_enabled = 'yes' === ($gateway_settings['enabled'] ?? 'no');
		$api_key_configured = !empty($current_profile['apiKey']);
		$hmac_configured = !empty($current_profile['hmacKey']);
		$webhook_token_configured = !empty($options['webhookToken']);
		$live_prefix_ready = $test_mode || Adyen_Endpoint_Resolver::is_valid_live_url_prefix($current_profile['liveUrlPrefix'] ?? '');
		$checkout_ready = $merchant_valid && $api_key_configured && $client_key_valid && $live_prefix_ready && $gateway_enabled;
		$webhook_ready = $hmac_configured && $webhook_token_configured;

		$current_environment = $test_mode ? 'test' : 'live';
		$profiles = [
			'test' => $this->get_public_environment($test_profile, true, $gateway_enabled),
			'live' => $this->get_public_environment($live_profile, false, $gateway_enabled),
		];

		return [
			'testMode' => !empty($options['testMode']),
			'merchantAccount' => $merchant_account,
			'apiKey' => '',
			'apiKeyConfigured' => $api_key_configured,
			'clientKey' => $client_key,
			'liveUrlPrefix' => sanitize_text_field($current_profile['liveUrlPrefix'] ?? ''),
			'captureMode' => 'manual' === ($options['captureMode'] ?? '') ? 'manual' : 'automatic',
			'debugLogging' => !empty($options['debugLogging']),
			'webhookUsername' => sanitize_text_field($options['webhookUsername'] ?? ''),
			'webhookPassword' => '',
			'webhookPasswordConfigured' => !empty($options['webhookPassword']),
			'hmacKey' => '',
			'hmacKeyConfigured' => $hmac_configured,
			'previousHmacKeyConfigured' => !empty($current_profile['previousHmacKey']),
			'webhookToken' => sanitize_text_field($options['webhookToken'] ?? ''),
			'validation' => [
				'merchantAccountValid' => $merchant_valid,
				'clientKeyValid' => $client_key_valid,
			],
			'environments' => $profiles,
			'readiness' => [
				'currentEnvironment' => $current_environment,
				'checkoutReady' => $checkout_ready,
				'webhookReady' => $webhook_ready,
				'environments' => [
					'test' => [
						'active' => 'test' === $current_environment,
						'items' => $profiles['test']['items'],
					],
					'live' => [
						'active' => 'live' === $current_environment,
						'items' => $profiles['live']['items'],
					],
				],
			],
			'diagnostics' => [
				'wordpressVersion' => get_bloginfo('version'),
				'woocommerceVersion' => defined('WC_VERSION') ? WC_VERSION : '',
				'phpVersion' => PHP_VERSION,
				'loggerSource' => Logger::SOURCE,
			],
		];
	}

	/** Build one non-secret environment profile for the administration UI. */
	private function get_public_environment(array $profile, $test_mode, $gateway_enabled)
	{
		$merchant_account = sanitize_text_field($profile['merchantAccount'] ?? '');
		$client_key = sanitize_text_field($profile['clientKey'] ?? '');
		$merchant_valid = $this->is_valid_merchant_account($merchant_account);
		$client_key_valid = $this->is_valid_client_key($client_key, $test_mode);
		$live_prefix_valid = $test_mode || Adyen_Endpoint_Resolver::is_valid_live_url_prefix($profile['liveUrlPrefix'] ?? '');

		return [
			'merchantAccount' => $merchant_account,
			'clientKey' => $client_key,
			'liveUrlPrefix' => $test_mode ? '' : sanitize_text_field($profile['liveUrlPrefix'] ?? ''),
			'apiKeyConfigured' => !empty($profile['apiKey']),
			'hmacKeyConfigured' => !empty($profile['hmacKey']),
			'previousHmacKeyConfigured' => !empty($profile['previousHmacKey']),
			'items' => [
				'merchantAccount' => $merchant_valid,
				'apiKey' => !empty($profile['apiKey']),
				'clientKey' => $client_key_valid,
				'liveUrlPrefix' => $live_prefix_valid,
				'gateway' => $gateway_enabled,
				'hmacKey' => !empty($profile['hmacKey']),
			],
		];
	}

	/**
	 * POST/PUT /settings
	 *
	 * @param \WP_REST_Request $request
	 * @return \WP_REST_Response
	 */
	public function update_settings($request)
	{
		$new_settings = $request->get_json_params();
		if (!is_array($new_settings)) {
			return new \WP_Error('invalid_settings', __('Settings must be a JSON object.', 'digital-apps-payments-for-adyen'), ['status' => 400]);
		}

		$existing_settings = Settings_Store::get();
		$allowed_settings = [
			'testMode',
			'merchantAccount',
			'apiKey',
			'clientKey',
			'liveUrlPrefix',
			'captureMode',
			'debugLogging',
			'webhookUsername',
			'webhookPassword',
			'hmacKey',
		];
		$new_settings = array_intersect_key($new_settings, array_flip($allowed_settings));

		$sanitized = [];
		foreach ($new_settings as $key => $value) {
			switch ($key) {
				case 'testMode':
				case 'debugLogging':
					$sanitized[$key] = rest_sanitize_boolean($value);
					break;
				case 'captureMode':
					$sanitized[$key] = 'manual' === $value ? 'manual' : 'automatic';
					break;
				case 'liveUrlPrefix':
					$sanitized[$key] = strtolower(sanitize_text_field($value));
					break;
				case 'apiKey':
				case 'webhookPassword':
				case 'hmacKey':
					if (is_string($value) && trim($value) !== '') {
						$sanitized[$key] = sanitize_text_field(trim($value));
					}
					break;
				default:
					$sanitized[$key] = sanitize_text_field($value);
			}
		}

		$target_test_mode = array_key_exists('testMode', $sanitized)
			? !empty($sanitized['testMode'])
			: !empty($existing_settings['testMode']);
		$merged_settings = $existing_settings;
		$merged_settings['testMode'] = $target_test_mode;
		foreach (['captureMode', 'debugLogging', 'webhookUsername', 'webhookPassword'] as $global_key) {
			if (array_key_exists($global_key, $sanitized)) {
				$merged_settings[$global_key] = $sanitized[$global_key];
			}
		}

		$current_environment = Settings_Store::get_environment($target_test_mode, $existing_settings);
		$environment_update = [];
		foreach (['merchantAccount', 'apiKey', 'clientKey', 'liveUrlPrefix'] as $environment_key) {
			if (array_key_exists($environment_key, $sanitized)) {
				$environment_update[$environment_key] = $sanitized[$environment_key];
			}
		}
		if (array_key_exists('hmacKey', $sanitized)) {
			$new_hmac_key = $sanitized['hmacKey'];
			if (!empty($current_environment['hmacKey']) && !hash_equals((string) $current_environment['hmacKey'], $new_hmac_key)) {
				$environment_update['previousHmacKey'] = $current_environment['hmacKey'];
			}
			$environment_update['hmacKey'] = $new_hmac_key;
		}
		$merged_settings = Settings_Store::set_environment($merged_settings, $target_test_mode, $environment_update);

		if (rest_sanitize_boolean($request->get_param('clearApiKey'))) {
			$merged_settings = Settings_Store::set_environment($merged_settings, $target_test_mode, ['apiKey' => '']);
		}
		if (rest_sanitize_boolean($request->get_param('clearWebhookPassword'))) {
			$merged_settings['webhookPassword'] = '';
		}
		if (rest_sanitize_boolean($request->get_param('clearHmacKey'))) {
			$merged_settings = Settings_Store::set_environment($merged_settings, $target_test_mode, [
				'hmacKey' => '',
				'previousHmacKey' => '',
			]);
		}

		$is_configuration_update = array_key_exists('testMode', $new_settings)
			|| array_key_exists('merchantAccount', $new_settings)
			|| array_key_exists('apiKey', $new_settings)
			|| array_key_exists('clientKey', $new_settings)
			|| array_key_exists('liveUrlPrefix', $new_settings);
		if ($is_configuration_update) {
			$validation_error = $this->validate_configuration($merged_settings, $target_test_mode);
			if (is_wp_error($validation_error)) {
				return $validation_error;
			}
		}

		if (isset($sanitized['hmacKey']) && !preg_match('/^[a-fA-F0-9]{64}$/', $sanitized['hmacKey'])) {
			return new \WP_Error('invalid_hmac_key', __('The HMAC key must be the 64-character hexadecimal key generated by Adyen.', 'digital-apps-payments-for-adyen'), ['status' => 400]);
		}

		$active_environment = Settings_Store::get_environment($target_test_mode, $merged_settings);
		$webhook_username = trim((string) ($merged_settings['webhookUsername'] ?? ''));
		$webhook_password = (string) ($merged_settings['webhookPassword'] ?? '');
		$is_webhook_update = array_key_exists('webhookUsername', $new_settings)
			|| array_key_exists('webhookPassword', $new_settings)
			|| array_key_exists('hmacKey', $new_settings)
			|| rest_sanitize_boolean($request->get_param('clearWebhookPassword'))
			|| rest_sanitize_boolean($request->get_param('clearHmacKey'));
		if ($is_webhook_update && (('' === $webhook_username) xor ('' === $webhook_password))) {
			return new \WP_Error('invalid_webhook_basic_auth', __('Basic Auth requires both a username and a password. Remove both fields to disable it.', 'digital-apps-payments-for-adyen'), ['status' => 400]);
		}

		if (empty($merged_settings['webhookToken'])) {
			$merged_settings['webhookToken'] = wp_generate_password(48, false, false);
		}
		Settings_Store::save($merged_settings);
		\DigitalApps\PaymentsForAdyen\Adyen_Payment_Method_Status::clear_cache();

		return rest_ensure_response([
			'success' => true,
			'message' => __('Settings saved.', 'digital-apps-payments-for-adyen'),
			'settings' => $this->get_public_settings(Settings_Store::set_environment($merged_settings, $target_test_mode, $active_environment)),
		]);
	}

	/**
	 * Verify the configured credentials and card capability with Adyen.
	 *
	 * @param \WP_REST_Request $request Request object.
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function test_connection($request)
	{
		unset($request);
		$settings = Settings_Store::get();
		$validation_error = $this->validate_configuration($settings);
		if (is_wp_error($validation_error)) {
			return $validation_error;
		}

		$result = (new Adyen_Client())->get_payment_methods_result();
		if (empty($result['success'])) {
			return rest_ensure_response([
				'success' => false,
				'status' => 'error',
				'message' => $result['message'] ?? __('Adyen could not verify these credentials.', 'digital-apps-payments-for-adyen'),
			]);
		}

		$types = array_values(array_filter(array_map(function ($method) {
			return is_array($method) ? ($method['type'] ?? null) : null;
		}, $result['paymentMethods'])));
		$card_enabled = in_array('scheme', $types, true);

		return rest_ensure_response([
			'success' => true,
			'status' => $card_enabled ? 'ready' : 'card_unavailable',
			'cardEnabled' => $card_enabled,
			'message' => $card_enabled
				? __('Connection successful. Credit and debit cards are available for this merchant account.', 'digital-apps-payments-for-adyen')
				: __('Connection successful, but Adyen did not return card payments for this request context.', 'digital-apps-payments-for-adyen'),
		]);
	}

	/**
	 * Validate the checkout configuration.
	 *
	 * @param array $settings Decrypted settings.
	 * @return true|\WP_Error
	 */
	private function validate_configuration(array $settings, $test_mode = null)
	{
		$profile = Settings_Store::get_environment($test_mode, $settings);
		$test_mode = !empty($profile['testMode']);
		$merchant_account = trim((string) ($profile['merchantAccount'] ?? ''));
		if (!$this->is_valid_merchant_account($merchant_account)) {
			if (false !== strpos($merchant_account, '@') || 0 === stripos($merchant_account, 'ws_')) {
				return new \WP_Error('invalid_merchant_account', __('Enter the Adyen merchant account, for example DigitalAppsECOM, not the ws_ API credential username.', 'digital-apps-payments-for-adyen'), ['status' => 400]);
			}
			return new \WP_Error('invalid_merchant_account', __('Enter a valid Adyen merchant account.', 'digital-apps-payments-for-adyen'), ['status' => 400]);
		}

		if (empty($profile['apiKey'])) {
			return new \WP_Error('missing_api_key', __('Enter an Adyen API key.', 'digital-apps-payments-for-adyen'), ['status' => 400]);
		}

		if (!$this->is_valid_client_key((string) ($profile['clientKey'] ?? ''), $test_mode)) {
			$prefix = $test_mode ? 'test_' : 'live_';
			/* translators: %s: Required Adyen client key prefix. */
			return new \WP_Error('invalid_client_key', sprintf(__('The client key must belong to the selected environment and begin with %s.', 'digital-apps-payments-for-adyen'), $prefix), ['status' => 400]);
		}

		if (!$test_mode && !Adyen_Endpoint_Resolver::is_valid_live_url_prefix($profile['liveUrlPrefix'] ?? '')) {
			return new \WP_Error('invalid_live_url_prefix', __('Enter the live URL prefix assigned in Adyen Customer Area, without a protocol or domain.', 'digital-apps-payments-for-adyen'), ['status' => 400]);
		}

		return true;
	}

	/**
	 * Check that a value looks like a merchant account rather than a credential username.
	 *
	 * @param string $merchant_account Merchant account value.
	 * @return bool
	 */
	private function is_valid_merchant_account($merchant_account)
	{
		return '' !== $merchant_account
			&& false === strpos($merchant_account, '@')
			&& 0 !== stripos($merchant_account, 'ws_')
			&& 1 === preg_match('/^[A-Za-z0-9][A-Za-z0-9._-]{1,127}$/', $merchant_account);
	}

	/**
	 * Check that a client key matches the selected Adyen environment.
	 *
	 * @param string $client_key Client key.
	 * @param bool   $test_mode Whether test mode is selected.
	 * @return bool
	 */
	private function is_valid_client_key($client_key, $test_mode)
	{
		$prefix = $test_mode ? 'test_' : 'live_';
		return 0 === strpos(trim($client_key), $prefix) && strlen(trim($client_key)) > strlen($prefix);
	}

	/**
	 * POST /capture-payment
	 *
	 * @param \WP_REST_Request $request
	 * @return \WP_REST_Response
	 */
	public function capture_payment($request)
	{
		$order_id = $request->get_param('order_id');
		$order = \wc_get_order($order_id);

		if (!$order) {
			return new \WP_REST_Response([
				'success' => false,
				'message' => __('Order not found.', 'digital-apps-payments-for-adyen'),
			], 404);
		}

		// Ensure the order is eligible for capture
		if ($order->get_status() !== 'on-hold' || $order->get_payment_method() !== 'digitalapps_payments_for_adyen_card') {
			return new \WP_REST_Response([
				'success' => false,
				'message' => __('Order is not eligible for manual capture.', 'digital-apps-payments-for-adyen'),
			], 400);
		}

		$psp_reference = $order->get_transaction_id();
		if (empty($psp_reference)) {
			return new \WP_REST_Response([
				'success' => false,
				'message' => __('Adyen PSP Reference not found for this order.', 'digital-apps-payments-for-adyen'),
			], 400);
		}

		$amount = $order->get_total();
		$currency = $order->get_currency();
		$operation_id = 'capture:full';
		$existing_operation = \DigitalApps\PaymentsForAdyen\Adyen_Operation::get($order, $operation_id);
		if (in_array($existing_operation['status'] ?? '', ['initiated', 'succeeded'], true)) {
			return new \WP_REST_Response([
				'success' => true,
				'message' => __('A capture request is already pending webhook confirmation.', 'digital-apps-payments-for-adyen'),
			], 200);
		}
		$idempotency_key = \DigitalApps\PaymentsForAdyen\Adyen_Operation::key_for_order($order, $operation_id);

		$adyen_client = Adyen_Client::for_order($order);
		$response = $adyen_client->capture($psp_reference, $amount, $currency, $idempotency_key);

		if (isset($response['status']) && $response['status'] === 'received') {
			\DigitalApps\PaymentsForAdyen\Adyen_Operation::mark($order, $operation_id, 'initiated', [
				'type' => 'capture',
				'pspReference' => sanitize_text_field($response['pspReference'] ?? ''),
				'originalReference' => $psp_reference,
			]);
			/* translators: %s: Adyen PSP reference. */
			$order->add_order_note(sprintf(__('Adyen manual capture requested. PSP reference: %s. Awaiting webhook confirmation.', 'digital-apps-payments-for-adyen'), $response['pspReference'] ?? $psp_reference));
			return new \WP_REST_Response([
				'success' => true,
				'message' => __('Capture request accepted. Awaiting webhook confirmation.', 'digital-apps-payments-for-adyen'),
				'pspReference' => $response['pspReference'] ?? $psp_reference,
			], 200);
		} else {
			$order->add_order_note(__('Adyen manual capture request was not accepted. Review the protected WooCommerce logs before retrying.', 'digital-apps-payments-for-adyen'));
			return new \WP_REST_Response([
				'success' => false,
				'message' => __('Adyen did not accept the capture request. Review the WooCommerce logs and retry when safe.', 'digital-apps-payments-for-adyen'),
			], 500);
		}
	}

	/**
	 * GET /logs
	 *
	 * @param \WP_REST_Request $request
	 * @return \WP_REST_Response
	 */
	public function get_logs($request)
	{
		$logger = new \DigitalApps\PaymentsForAdyen\Logger();
		return rest_ensure_response($logger->get_logs());
	}

	/**
	 * DELETE /logs
	 *
	 * @param \WP_REST_Request $request
	 * @return \WP_REST_Response
	 */
	public function clear_logs($request)
	{
		$logger = new \DigitalApps\PaymentsForAdyen\Logger();
		$logger->clear_logs();
		return rest_ensure_response([]);
	}

}
