<?php

namespace DigitalApps\PaymentsForAdyen\Api;

defined('ABSPATH') || exit;

use WP_REST_Controller;
use WP_REST_Server;
use WP_REST_Response;
use DigitalApps\PaymentsForAdyen\Adyen_Client;
use DigitalApps\PaymentsForAdyen\Adyen_Result;
use DigitalApps\PaymentsForAdyen\Adyen_Session_Manager;
use DigitalApps\PaymentsForAdyen\Logger;
use DigitalApps\PaymentsForAdyen\Settings_Store;

/** One-time Advanced-flow `/payments/details` continuation endpoint. */
class Adyen_Details_Controller extends WP_REST_Controller
{
	protected $namespace = 'digitalapps-payments-for-adyen/v1';
	protected $rest_base = 'submit-additional-details';

	/** @var Logger */
	private $logger;

	public function __construct()
	{
		$this->logger = new Logger();
	}

	public function register_routes()
	{
		register_rest_route($this->namespace, '/' . $this->rest_base, [
			[
				'methods' => WP_REST_Server::CREATABLE,
				'callback' => [$this, 'submit_details'],
				'permission_callback' => '__return_true',
				'args' => [
					'session_key' => [
						'required' => true,
						'type' => 'string',
						'validate_callback' => [$this, 'validate_session_key'],
					],
					'details' => [
						'required' => true,
						'type' => 'object',
					],
				],
			],
		]);
	}

	public function validate_session_key($session_key)
	{
		return is_string($session_key)
			&& 1 === preg_match('/^digitalapps_payments_for_adyen_3ds_[A-Za-z0-9]{32}$/', $session_key);
	}

	/** Submit a bound, unexpired, one-time continuation to Adyen. */
	public function submit_details($request)
	{
		$session_key = (string) $request->get_param('session_key');
		$details = $request->get_param('details');
		if (!is_array($details) || empty($details)) {
			return new \WP_Error('invalid_details', __('Payment authentication data is invalid.', 'digital-apps-payments-for-adyen'), ['status' => 400]);
		}
		$details = map_deep($details, 'sanitize_text_field');

		$manager = new Adyen_Session_Manager();
		$session = $manager->claim_session($session_key);
		if (!$session) {
			return new \WP_Error(
				'continuation_unavailable',
				__('This payment authentication request is invalid, expired, or already used.', 'digital-apps-payments-for-adyen'),
				['status' => 409]
			);
		}

		$order = wc_get_order($session['order_id']);
		if (!$order || 'digitalapps_payments_for_adyen_card' !== $order->get_payment_method()) {
			$manager->delete_session($session_key);
			return new \WP_Error('order_unavailable', __('The payment order is unavailable.', 'digital-apps-payments-for-adyen'), ['status' => 404]);
		}

		$response = Adyen_Client::for_order($order)->submit_additional_details(
			$session['payment_data'],
			$details,
			'details:' . $session_key
		);
		$classification = Adyen_Result::classify($response);

		if (!empty($response['error'])) {
			$manager->release_session($session_key);
			$this->logger->log('ERROR', [
				'message' => 'Adyen action details request failed',
				'order_id' => $order->get_id(),
				'error_code' => $response['errorCode'] ?? '',
				'http_status' => $response['httpStatus'] ?? 0,
			]);
			return new \WP_Error(
				'details_request_failed',
				Adyen_Result::shopper_message(Adyen_Result::ERROR),
				['status' => 502]
			);
		}

		$capture_mode = (string) $order->get_meta('_digitalapps_payments_for_adyen_capture_mode', true);
		if (!in_array($capture_mode, ['automatic', 'manual'], true)) {
			$settings = Settings_Store::get();
			$capture_mode = 'manual' === ($settings['captureMode'] ?? '') ? 'manual' : 'automatic';
		}
		Adyen_Result::apply_to_order($order, $response, $capture_mode);
		$continuation_token = '';

		if (Adyen_Result::ACTION === $classification) {
			$continuation_token = $manager->create_session(
				$order->get_id(),
				$response['paymentData'] ?? $session['payment_data'],
				$response['action']
			);
			if (!$continuation_token) {
				$manager->release_session($session_key);
				return new \WP_Error('continuation_create_failed', Adyen_Result::shopper_message(Adyen_Result::ERROR), ['status' => 500]);
			}
		}

		$manager->delete_session($session_key);
		$this->logger->log('INFO', [
			'message' => 'Adyen action details result processed',
			'order_id' => $order->get_id(),
			'classification' => $classification,
		]);

		return new WP_REST_Response([
			'success' => true,
			'classification' => $classification,
			'adyen_response' => Adyen_Result::public_response($response),
			'continuation_token' => $continuation_token,
			'redirect_url' => in_array($classification, [Adyen_Result::AUTHORISED, Adyen_Result::PENDING], true)
				? $order->get_checkout_order_received_url()
				: '',
			'message' => Adyen_Result::shopper_message($classification),
		], 200);
	}
}
