<?php
/**
 * CommerceBird Settings Ajax Handler
 *
 * @package CommerceBird\Admin\Actions\Ajax
 */
namespace CommerceBird\Admin\Actions\Ajax;

use CommerceBird\Admin\Connectors\Connector;
use CommerceBird\Admin\Traits\AjaxRequest;
use CommerceBird\Admin\Traits\OptionStatus;
use CommerceBird\Admin\Traits\Singleton;
use CommerceBird\Admin\Traits\LogWriter;
use WpOrg\Requests\Exception;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}
final class SettingsAjax {


	use Singleton;
	use AjaxRequest;
	use OptionStatus;
	use LogWriter;

	private const FORMS = array(
		'settings' => array(
			'id',
			'email',
			'token',
		),
	);

	private const ACTIONS = array(
		'get_subscription' => 'subscription_get',
		'get_settings'     => 'settings_get',
		'save_settings'    => 'settings_set',
		'reset_settings'   => 'settings_reset',
	);

	private const OPTIONS = array(
		'settings' => array(
			'token' => 'commercebird-exact-online-token',
			'id'    => 'commercebird-subscription-id',
			'email' => 'commercebird-woo-webhook-status',
		),
	);

	/**
	 * SettingsAjax constructor.
	 */
	public function __construct() {
		$this->load_actions();
	}

	/**
	 * Retrieves the subscription details with enhanced security.
	 *
	 * @return void
	 */
	public function subscription_get(): void {
		$this->verify();

		// Validate subscription ID and email ownership.
		$subscription_id = get_option( self::OPTIONS['settings']['id'], 0 );
		$stored_email    = get_option( self::OPTIONS['settings']['email'], '' );

		if ( empty( $subscription_id ) || empty( $stored_email ) ) {
			$this->errors = array( 'message' => 'Missing subscription ID or email' );
			$this->serve();
			return;
		}

		$subscription_data = $this->get_subscription_data();

		// Server-side email validation - critical security check.
		if ( isset( $subscription_data['billing']['email'] ) ) {
			if ( $subscription_data['billing']['email'] !== $stored_email ) {
				$this->errors = array(
					'message' => 'Email mismatch: Access denied',
					'code'    => 'email_mismatch',
				);

				// Log potential security breach attempt.
			// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Required for security logging.
				error_log(
					sprintf(
						'CommerceBird Security: Email mismatch attempt. User ID: %d, IP: %s, Stored: %s, Attempted: %s',
						get_current_user_id(),
						isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : 'unknown',
						$stored_email,
						$subscription_data['billing']['email']
					)
				); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Security logging.

				$this->serve();
				return;
			}
		} else {
			// If no subscription data or no billing email, return empty structure.
			$this->response = $this->get_empty_subscription_structure();
			$this->serve();
			return;
		}

		// Remove sensitive data before sending to frontend.
		$safe_data      = $this->sanitize_subscription_data( $subscription_data );
		$this->response = $safe_data;
		$this->serve();
	}

	/**
	 * @description Function to get subscription data from CommerceBird API.
	 */
	public function get_subscription_data(): array {
		$transient = get_transient( 'subscription_details' );
		if ( ! empty( $transient ) ) {
			return $transient;
		}
		$subscription_id = get_option( self::OPTIONS['settings']['id'], 0 );
		$data            = ( new Connector() )->get_subscription(
			array(
				'subscriptionId' => $subscription_id,
			)
		);
		if ( is_wp_error( $data ) ) {
			return array(
				'status'  => 'error',
				'message' => $data->get_error_message(),
			);
		}
		if ( ! empty( $data ) && 'error' !== $data['status'] ) {
			$data['variation_id'] = array_column( isset( $data['line_items'] ) ? $data['line_items'] : array(), 'variation_id' );
			$plan_names           = array_column( isset( $data['line_items'] ) ? $data['line_items'] : array(), 'name' );
			$data['plan']         = implode( ', ', $plan_names );
			set_transient( 'subscription_details', $data, WEEK_IN_SECONDS );

			// Store subscription status for admin notices.
			update_option( 'commercebird-subscription-status', $data['status'], false );
		} else {
			$data = array(
				'status'  => 'error',
				'message' => $data['message'],
			);
		}
		return $data;
	}


	/**
	 * Resets the settings details.
	 *
	 * @return void
	 */
	public function settings_reset(): void {
		$this->verify();
		delete_option( self::OPTIONS['settings']['token'] );
		delete_option( self::OPTIONS['settings']['id'] );
		delete_option( self::OPTIONS['settings']['email'] );
		$this->response = array( 'message' => 'Reset successfully!' );
		$this->serve();
	}

	/**
	 * Retrieves the settings details.
	 *
	 * @return void
	 */
	public function settings_get(): void {
		$this->verify();
		$token                   = get_option( self::OPTIONS['settings']['token'], '' );
		$id                      = get_option( self::OPTIONS['settings']['id'], '' );
		$email                   = get_option( self::OPTIONS['settings']['email'], '' );
		$this->response['token'] = $token;
		$this->response['id']    = $id;
		$this->response['email'] = $email;
		$this->serve();
	}

	/**
	 * Sets the settings for the class with enhanced validation.
	 *
	 * @return void
	 */
	public function settings_set(): void {
		$this->verify( self::FORMS['settings'] );

		try {
			if ( $this->data ) {
				// Enhanced input validation.
				$id    = $this->validate_subscription_id( $this->data['id'] ?? '' );
				$email = $this->validate_email( $this->data['email'] ?? '' );
				$token = $this->validate_token( $this->data['token'] ?? '' );

				if ( ! $id || ! $email || ! $token ) {
					$this->errors = array( 'message' => 'Invalid input data provided' );
					$this->serve();
					return;
				}

				// Update options with validated data.
				update_option( self::OPTIONS['settings']['id'], $id );
				update_option( self::OPTIONS['settings']['email'], $email );
				update_option( self::OPTIONS['settings']['token'], $token );

				// Clear cached subscription data when settings change.
				delete_transient( 'subscription_details' );

				$this->response = array( 'message' => 'Settings saved successfully' );
			} else {
				$this->errors = array( 'message' => 'No data provided' );
			}
		} catch ( Exception $exception ) {
			$this->errors = array( 'message' => 'Failed to save settings' );
		}
		$this->serve();
	}

	/**
	 * Validate subscription ID format.
	 *
	 * @param string $id Subscription ID to validate.
	 * @return string|false Validated ID or false if invalid.
	 */
	private function validate_subscription_id( $id ) {
		$id = sanitize_text_field( $id );
		return is_numeric( $id ) && $id > 0 ? $id : false;
	}

	/**
	 * Validate email format and sanitize.
	 *
	 * @param string $email Email to validate.
	 * @return string|false Validated email or false if invalid.
	 */
	private function validate_email( $email ) {
		$email = sanitize_email( $email );
		return is_email( $email ) ? $email : false;
	}

	/**
	 * Validate token format.
	 *
	 * @param string $token Token to validate.
	 * @return string|false Validated token or false if invalid.
	 */
	private function validate_token( $token ) {
		$token = sanitize_text_field( $token );
		// Basic token validation - adjust pattern as needed.
		return preg_match( '/^[a-zA-Z0-9_-]+$/', $token ) ? $token : false;
	}

	/**
	 * Sanitize subscription data before sending to frontend.
	 *
	 * @param array $data Raw subscription data.
	 * @return array Sanitized data.
	 */
	/**
	 * Sanitize subscription data before sending to frontend.
	 *
	 * @param array $data Raw subscription data.
	 * @return array Sanitized data.
	 */
	private function sanitize_subscription_data( $data ) {
		if ( ! is_array( $data ) ) {
			return array();
		}

		// Remove or hash sensitive billing information.
		if ( isset( $data['billing'] ) ) {
			$safe_billing = array();
			// Include safe billing fields that frontend expects.
			$safe_billing_fields = array( 'first_name', 'company', 'country', 'email' );
			foreach ( $safe_billing_fields as $billing_field ) {
				if ( isset( $data['billing'][ $billing_field ] ) ) {
					if ( 'email' === $billing_field ) {
						$safe_billing[ $billing_field ] = sanitize_email( $data['billing'][ $billing_field ] );
					} else {
						$safe_billing[ $billing_field ] = sanitize_text_field( $data['billing'][ $billing_field ] );
					}
				}
			}
			$data['billing'] = $safe_billing;
		}

		// Define all safe fields that the frontend expects.
		$safe_fields = array(
			'status',
			'plan',
			'currency',
			'total',
			'needs_payment',
			'next_payment_date_gmt',
			'payment_url',
			'variation_id',
		);
		$sanitized   = array();

		foreach ( $safe_fields as $field ) {
			if ( isset( $data[ $field ] ) ) {
				if ( is_array( $data[ $field ] ) ) {
					$sanitized[ $field ] = array_map( 'sanitize_text_field', $data[ $field ] );
				} elseif ( 'needs_payment' === $field ) {
					$sanitized[ $field ] = (bool) $data[ $field ];
				} else {
					$sanitized[ $field ] = sanitize_text_field( $data[ $field ] );
				}
			}
		}

		// Handle fee_lines array specially since it contains objects.
		if ( isset( $data['fee_lines'] ) && is_array( $data['fee_lines'] ) ) {
			$sanitized['fee_lines'] = array();
			foreach ( $data['fee_lines'] as $fee_line ) {
				if ( is_array( $fee_line ) ) {
					$sanitized_fee = array();
					$fee_fields    = array( 'id', 'name', 'tax_class', 'tax_status' );
					foreach ( $fee_fields as $fee_field ) {
						if ( isset( $fee_line[ $fee_field ] ) ) {
							if ( 'id' === $fee_field ) {
								$sanitized_fee[ $fee_field ] = (int) $fee_line[ $fee_field ];
							} else {
								$sanitized_fee[ $fee_field ] = sanitize_text_field( $fee_line[ $fee_field ] );
							}
						}
					}
					$sanitized['fee_lines'][] = $sanitized_fee;
				}
			}
		}

		// Include billing data.
		if ( isset( $data['billing'] ) ) {
			$sanitized['billing'] = $data['billing'];
		}

		return $sanitized;
	}

	/**
	 * Get empty subscription structure that matches frontend expectations.
	 *
	 * @return array Empty subscription structure.
	 */
	private function get_empty_subscription_structure(): array {
		return array(
			'status'                => '',
			'currency'              => '',
			'total'                 => '',
			'fee_lines'             => array(),
			'payment_url'           => '',
			'needs_payment'         => false,
			'next_payment_date_gmt' => '',
			'variation_id'          => array(),
			'plan'                  => '',
			'billing'               => array(
				'first_name' => '',
				'company'    => '',
				'country'    => '',
				'email'      => '',
			),
		);
	}

	/**
	 * Function to get the token from the settings.
	 */
	public function get_token() {
		return get_option( self::OPTIONS['settings']['token'], '' );
	}
}
