<?php

namespace SplitPay\Integrations\WooCommerce;

use SplitPay\Integrations\WooCommerce\Adapters\WCOrderAdapter;
use SplitPay\Integrations\WooCommerce\Gateways\WCStripeGateway;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class WebhookHandler {

	private WCStripeGateway $gateway;
	private string $secret;
	private string $logPrefix = '[Split Pay WebhookHandler] ';

	public function __construct( WCStripeGateway $gateway ) {
		$this->gateway = $gateway;
		$this->secret  = $gateway->getWebhookSecret();
	}

	/**
	 * Main handler for the woocommerce_api_wc_stripe action.
	 * Hooked at priority 9 to run before WC_Stripe_Webhook_Handler.
	 */
	public function handleWebhook(): void {
		if ( 'POST' !== ( wp_unslash( $_SERVER['REQUEST_METHOD'] ?? '' ) )
			|| ! isset( $_GET['wc-api'] )
			|| 'wc_stripe' !== $_GET['wc-api']
		) {
			return;
		}

		$wc_stripe_webhook_handler = new \WC_Stripe_Webhook_Handler();

		$request_body    = file_get_contents( 'php://input' );
		$request_headers = array_change_key_case( $wc_stripe_webhook_handler->get_request_headers(), CASE_UPPER );

		if ( ! $this->webhookForThisSite( $request_body ) ) {
			status_header( 200 );
			exit;
		}

		if ( $this->isValidRequest( $request_headers, $request_body ) ) {
			$wc_stripe_webhook_handler->process_webhook( $request_body );
			$this->log( 'after process webhooks ' . wp_json_encode( $request_body ) );
			$this->processTransfersFromWebhook( $request_body );

			status_header( 200 );
			exit;
		}

		$this->log( 'Incoming webhook failed validation: ' . wp_json_encode( $request_body ) );
		status_header( 400 );
		exit;
	}

	public function isValidRequest( ?array $headers = null, ?string $body = null ): bool {
		if ( $headers === null || $body === null ) {
			return false;
		}

		if ( ! empty( $headers['USER-AGENT'] ) && ! preg_match( '/Stripe/', $headers['USER-AGENT'] ) ) {
			return false;
		}

		// PT-fix-3.7.5 (security #17): previous behaviour was to short-circuit
		// to `return true` when `$this->secret` was empty — i.e. on every
		// fresh install before the merchant configured a Stripe webhook
		// secret. That left the endpoint unauthenticated: an attacker could
		// POST forged webhook payloads to /?wc-api=wc_stripe and trigger
		// real transfers, fake account.application.deauthorized events,
		// or fake charge.dispute.created events. Stripe webhooks MUST be
		// signature-verified — there is no safe "soft mode" here. If the
		// merchant hasn't configured a secret, reject with 400 + log a
		// loud warning so the merchant sees it in WC > Status > Logs.
		if ( empty( $this->secret ) ) {
			$this->log( 'Webhook rejected: no webhook_secret configured. Configure it in WooCommerce → Stripe → Webhook secret to enable webhook processing. Endpoint will reject all events until then.' );
			return false;
		}

		if ( empty( $headers['STRIPE-SIGNATURE'] ) ) {
			return false;
		}

		try {
			\Stripe\Webhook::constructEvent( $body, $headers['STRIPE-SIGNATURE'], $this->secret );
		} catch ( \Stripe\Exception\SignatureVerificationException $e ) {
			$this->log( 'Webhook signature verification failed: ' . $e->getMessage() );
			return false;
		} catch ( \Exception $e ) {
			$this->log( 'Webhook verification exception: ' . $e->getMessage() );
			return false;
		}

		return true;
	}

	public function webhookForThisSite( string $body ): bool {
		$source_id = $this->extractSourceId( $body );

		if ( empty( $source_id ) ) {
			$this->log( 'source_id not found' );
			return false;
		}

		if ( ! class_exists( 'WC_Stripe_Helper' ) ) {
			return false;
		}

		$order = \WC_Stripe_Helper::get_order_by_source_id( $source_id );
		return (bool) $order;
	}

	public function getEventType( string $body ): ?string {
		$decoded = json_decode( $body );
		if ( ! $decoded || empty( $decoded->type ) ) {
			return null;
		}
		return strtolower( $decoded->type );
	}

	/**
	 * Trigger the TransferEngine after a successful payment_intent webhook.
	 */
	private function processTransfersFromWebhook( string $requestBody ): void {
		$event_type = $this->getEventType( $requestBody );
		$this->log( 'transfer_controller event_type ' . wp_json_encode( $event_type ) );

		if ( $event_type !== 'payment_intent.succeeded' ) {
			return;
		}

		$decoded = json_decode( $requestBody );
		if ( ! $decoded || empty( $decoded->data->object->metadata->order_id ) ) {
			return;
		}

		$order_id = $decoded->data->object->metadata->order_id;
		$order    = wc_get_order( $order_id );
		if ( ! $order ) {
			$this->log( 'Order not found: ' . $order_id );
			return;
		}

		$adapter = new WCOrderAdapter( $order, $requestBody );

		if ( $adapter->isTransferProcessed() ) {
			$this->log( 'Transfer already done for order ' . $order_id );
			return;
		}

		$chargeId = $this->gateway->resolveChargeId( $requestBody );
		if ( empty( $chargeId ) ) {
			$this->log( 'Could not resolve charge ID for order ' . $order_id );
			return;
		}

		$secretKey = method_exists( $this->gateway, 'getTransferSecretKey' ) ? $this->gateway->getTransferSecretKey() : $this->gateway->getSecretKey();
		if ( empty( $secretKey ) ) {
			$this->log( 'No transfer API key for order ' . $order_id . ', falling back to legacy.' );
			$this->processTransfersLegacy( $requestBody );
			return;
		}

		try {
			if ( class_exists( '\SplitPay\Core\TransferEngine' ) ) {
				$stripe         = new \SplitPay\Core\StripeService( $secretKey );
				$logger         = new \SplitPay\Core\TransferLogger();
				$connectedAccts = new \SplitPay\Core\ConnectedAccounts();
				$engine         = new \SplitPay\Core\TransferEngine( $stripe, $logger, $connectedAccts );
				$engine->processTransfers( $adapter, $this->gateway, 'woocommerce', $chargeId );
			} else {
				$this->processTransfersLegacy( $requestBody );
			}
		} catch ( \Exception $e ) {
			$this->log( 'Transfer processing exception for order ' . $order_id . ': ' . $e->getMessage() );
		}
	}

	/**
	 * Fallback: delegate to the old BSD_SCSP_Main_Plugin transfer_controller
	 * until TransferEngine is fully wired.
	 */
	private function processTransfersLegacy( string $requestBody ): void {
		$legacyClass = '\BSD_Split_Pay_Stripe_Connect_Woo\Inc\BSD_SCSP_Main_Plugin';
		if ( class_exists( $legacyClass ) ) {
			$legacy = new $legacyClass();
			if ( method_exists( $legacy, 'transfer_controller' ) ) {
				$legacy->transfer_controller( $requestBody );
			}
		}
	}

	private function extractSourceId( string $body ): string {
		$decoded    = json_decode( $body );
		$event_type = $this->getEventType( $body );

		if ( ! $decoded || ! $event_type ) {
			return '';
		}

		switch ( $event_type ) {
			case 'charge.succeeded':
				return isset( $decoded->data->object->source->id )
					? $decoded->data->object->source->id
					: '';

			case 'payment_intent.succeeded':
				$source = $decoded->data->object->source ?? '';
				if ( empty( $source ) ) {
					$source = $decoded->data->object->payment_method ?? '';
				}
				return (string) $source;

			case 'source.chargeable':
				return isset( $decoded->data->object->id )
					? $decoded->data->object->id
					: '';
		}

		return '';
	}

	private function log( string $message ): void {
		self::safe_wcs_log( $this->logPrefix . $message );
	}

	/**
	 * See WooCommerceIntegration::safe_wcs_log() for full rationale.
	 *
	 * @since 3.7.3
	 */
	private static function safe_wcs_log( string $message ): void {
		if ( class_exists( '\WC_Stripe_Logger' ) ) {
			if ( method_exists( '\WC_Stripe_Logger', 'log' ) ) {
				\WC_Stripe_Logger::log( $message );
				return;
			}
			if ( method_exists( '\WC_Stripe_Logger', 'debug' ) ) {
				\WC_Stripe_Logger::debug( $message );
				return;
			}
		}
		if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { error_log( $message ); } // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
	}
}
