<?php // phpcs:ignore WordPress.Files.FileName.NotHyphenatedLowercase, WordPress.Files.FileName.InvalidClassFileName
/**
 * AJAX controller for the Snooze feature.
 *
 * Handles `autoship_snooze_order` (apply snooze, including re-snooze)
 * and `autoship_cancel_snooze` (resume an already-snoozed order via
 * SafeActivate). Validates input, enforces the merchant-configured
 * max-duration cap, and writes a one-shot post-snooze notice transient
 * for the detail-page banner to consume.
 *
 * @package Autoship\Modules\Snooze\Controllers
 * @since   2.13.0
 */

namespace Autoship\Modules\Snooze\Controllers;

use Autoship\Core\CredentialsInterface;
use Autoship\Core\EnvironmentInterface;
use Autoship\Domain\Snooze\SnoozeStatus;
use Autoship\Services\Logging\LoggerInterface;
use Autoship\Services\QPilot\QPilotServiceInterface;
use Autoship\Services\Snooze\Interfaces\SnoozeSettingsInterface;
use Exception;

/**
 * AJAX endpoint handlers for the Snooze feature.
 */
class SnoozeAjaxController {

	/**
	 * Snooze settings reader.
	 *
	 * @var SnoozeSettingsInterface
	 */
	private SnoozeSettingsInterface $settings;

	/**
	 * QPilot service client.
	 *
	 * @var QPilotServiceInterface
	 */
	private QPilotServiceInterface $qpilot;

	/**
	 * Credentials reader.
	 *
	 * @var CredentialsInterface
	 */
	private CredentialsInterface $credentials;

	/**
	 * Logger instance.
	 *
	 * @var LoggerInterface
	 */
	private LoggerInterface $logger;

	/**
	 * Environment service.
	 *
	 * @var EnvironmentInterface
	 */
	private EnvironmentInterface $environment;

	/**
	 * Constructor.
	 *
	 * @param SnoozeSettingsInterface $settings    Snooze settings reader.
	 * @param QPilotServiceInterface  $qpilot      QPilot service client.
	 * @param CredentialsInterface    $credentials Credentials reader.
	 * @param LoggerInterface         $logger      Logger for AJAX failures.
	 * @param EnvironmentInterface    $environment Environment service.
	 */
	public function __construct( SnoozeSettingsInterface $settings, QPilotServiceInterface $qpilot, CredentialsInterface $credentials, LoggerInterface $logger, EnvironmentInterface $environment ) {
		$this->settings    = $settings;
		$this->qpilot      = $qpilot;
		$this->credentials = $credentials;
		$this->logger      = $logger;
		$this->environment = $environment;
	}

	/**
	 * Handle the snooze-order AJAX request.
	 *
	 * Validates parameters, enforces the max-duration cap, calls the
	 * QPilot Snooze endpoint (with a SafeActivate first when re-snoozing
	 * an already-snoozed order), and sets the post-snooze notice
	 * transient on success.
	 */
	public function snooze_order(): void {
		check_ajax_referer( 'autoship-snooze-nonce', 'nonce' );

		$order_id      = isset( $_POST['order_id'] ) ? absint( $_POST['order_id'] ) : 0;
		$duration      = isset( $_POST['duration'] ) ? absint( $_POST['duration'] ) : 0;
		$duration_type = isset( $_POST['duration_type'] ) ? sanitize_text_field( wp_unslash( $_POST['duration_type'] ) ) : '';
		$custom_date   = isset( $_POST['custom_date'] ) ? sanitize_text_field( wp_unslash( $_POST['custom_date'] ) ) : '';
		$is_snoozed    = isset( $_POST['is_snoozed'] ) && 'true' === $_POST['is_snoozed'];

		if ( empty( $order_id ) || empty( $duration_type ) ) {
			wp_send_json_error(
				array( 'message' => __( 'Invalid snooze parameters.', 'autoship' ) ),
				400
			);
		}

		if ( ! in_array( $duration_type, array( 'Days', 'Weeks', 'Months', 'Custom' ), true ) ) {
			wp_send_json_error(
				array( 'message' => __( 'Invalid duration type.', 'autoship' ) ),
				400
			);
		}

		if ( null === $this->credentials->get_site_id() ) {
			wp_send_json_error(
				array( 'message' => __( 'Site not configured.', 'autoship' ) ),
				500
			);
		}

		$max_days = $this->settings->get_max_duration_days();

		if ( 'Custom' === $duration_type ) {
			$resolved = $this->resolve_custom_date( $custom_date, $max_days );

			$snooze_until_utc = $resolved['snooze_until_utc'];
			$duration         = $resolved['duration'];
			$duration_type    = $resolved['duration_type'];
		} else {
			if ( empty( $duration ) ) {
				wp_send_json_error(
					array( 'message' => __( 'Invalid snooze parameters.', 'autoship' ) ),
					400
				);
			}

			$this->enforce_preset_max_duration( $duration, $duration_type, $max_days );
			$snooze_until_utc = SnoozeStatus::calculate_resume_date( $duration, $duration_type );
		}

		try {
			// Re-snooze: clear the existing snooze first.
			if ( $is_snoozed ) {
				try {
					$this->qpilot->safe_activate_scheduled_order( $order_id );
				} catch ( Exception $e ) {
					$this->logger->error(
						__( 'Autoship Snooze', 'autoship' ),
						sprintf( 'Failed to clear existing snooze on order %d before re-snooze. Error: %s', $order_id, $e->getMessage() )
					);

					wp_send_json_error(
						array( 'message' => __( 'Failed to clear existing snooze before applying new duration.', 'autoship' ) ),
						500
					);
				}
			}

			$this->qpilot->snooze_scheduled_order( $order_id, $snooze_until_utc, $duration, $duration_type );

			$resume_date = date_i18n( get_option( 'date_format' ), strtotime( $snooze_until_utc ) );

			$user_id = $this->environment->get_current_user_id();
			if ( ! empty( $user_id ) ) {
				set_transient(
					'autoship_snooze_notice_' . $user_id . '_' . $order_id,
					array(
						'resume_date'   => $resume_date,
						'snooze_until'  => $snooze_until_utc,
						'duration'      => $duration,
						'duration_type' => $duration_type,
					),
					5 * MINUTE_IN_SECONDS
				);
			}

			wp_send_json_success(
				array(
					'message'       => sprintf(
						/* translators: %s is the formatted resume date */
						__( 'Snoozed until %s', 'autoship' ),
						$resume_date
					),
					'snooze_until'  => $snooze_until_utc,
					'resume_date'   => $resume_date,
					'duration'      => $duration,
					'duration_type' => $duration_type,
				)
			);
		} catch ( Exception $e ) {
			$this->logger->error(
				__( 'Autoship Snooze', 'autoship' ),
				sprintf( 'Snooze AJAX failed for order %d. Error: %s', $order_id, $e->getMessage() )
			);

			$code    = (int) $e->getCode();
			$message = $e->getMessage();

			wp_send_json_error(
				array( 'message' => '' !== $message ? $message : __( 'Failed to snooze subscription. Please try again.', 'autoship' ) ),
				$code > 0 ? $code : 500
			);
		}
	}

	/**
	 * Handle the cancel-snooze ("Resume Early") AJAX request.
	 *
	 * Calls the QPilot SafeActivate endpoint to clear all snooze metadata
	 * and return the order to Active.
	 */
	public function cancel_snooze(): void {
		check_ajax_referer( 'autoship-snooze-nonce', 'nonce' );

		$order_id = isset( $_POST['order_id'] ) ? absint( $_POST['order_id'] ) : 0;

		if ( empty( $order_id ) ) {
			wp_send_json_error(
				array( 'message' => __( 'Invalid order ID.', 'autoship' ) ),
				400
			);
		}

		if ( null === $this->credentials->get_site_id() ) {
			wp_send_json_error(
				array( 'message' => __( 'Site not configured.', 'autoship' ) ),
				500
			);
		}

		try {
			$this->qpilot->safe_activate_scheduled_order( $order_id );

			wp_send_json_success(
				array(
					'message' => __( 'Subscription resumed successfully.', 'autoship' ),
					'status'  => 'Active',
				)
			);
		} catch ( Exception $e ) {
			$this->logger->error(
				__( 'Autoship Snooze', 'autoship' ),
				sprintf( 'Cancel snooze AJAX failed for order %d. Error: %s', $order_id, $e->getMessage() )
			);

			$code    = (int) $e->getCode();
			$message = $e->getMessage();

			wp_send_json_error(
				array( 'message' => '' !== $message ? $message : __( 'Failed to resume subscription. Please try again.', 'autoship' ) ),
				$code > 0 ? $code : 500
			);
		}
	}

	/**
	 * Validate a custom-date snooze and convert it to days for the API.
	 *
	 * Sends a JSON error response and exits if validation fails.
	 *
	 * @param string $custom_date Date string from the request (Y-m-d).
	 * @param int    $max_days    Maximum allowed snooze in days (0 = no cap).
	 *
	 * @return array{snooze_until_utc:string,duration:int,duration_type:string}
	 */
	private function resolve_custom_date( string $custom_date, int $max_days ): array {
		if ( ! $this->settings->is_custom_date_allowed() ) {
			wp_send_json_error(
				array( 'message' => __( 'Custom resume dates are not allowed.', 'autoship' ) ),
				400
			);
		}

		if ( empty( $custom_date ) ) {
			wp_send_json_error(
				array( 'message' => __( 'Please select a resume date before confirming.', 'autoship' ) ),
				400
			);
		}

		$today_ts  = strtotime( gmdate( 'Y-m-d' ) );
		$custom_ts = strtotime( $custom_date );

		if ( false === $custom_ts || $custom_ts <= $today_ts ) {
			wp_send_json_error(
				array( 'message' => __( 'The selected date must be in the future.', 'autoship' ) ),
				400
			);
		}

		$days_until = (int) round( ( $custom_ts - $today_ts ) / DAY_IN_SECONDS );

		if ( $max_days > 0 && $days_until > $max_days ) {
			wp_send_json_error(
				array(
					'message' => sprintf(
						/* translators: %d is the maximum number of days */
						__( 'The resume date cannot be more than %d days from today.', 'autoship' ),
						$max_days
					),
				),
				400
			);
		}

		return array(
			'snooze_until_utc' => gmdate( 'Y-m-d\TH:i:s\Z', $custom_ts ),
			'duration'         => $days_until,
			'duration_type'    => 'Days',
		);
	}

	/**
	 * Enforce the max-duration cap on a preset duration.
	 *
	 * Sends a JSON error response and exits if the duration exceeds the cap.
	 *
	 * @param int    $duration      The duration value.
	 * @param string $duration_type The duration type: Days, Weeks, or Months.
	 * @param int    $max_days      Maximum allowed snooze in days (0 = no cap).
	 */
	private function enforce_preset_max_duration( int $duration, string $duration_type, int $max_days ): void {
		if ( ! SnoozeStatus::exceeds_max_duration( $duration, $duration_type, $max_days ) ) {
			return;
		}

		wp_send_json_error(
			array(
				'message' => sprintf(
					/* translators: %d is the maximum number of days */
					__( 'This duration exceeds the maximum allowed snooze of %d days.', 'autoship' ),
					$max_days
				),
			),
			400
		);
	}
}
