<?php
/**
 * Handler for country routing rules via admin-post.php and wp-ajax.
 *
 * @since   1.0.0
 * @package DonationRouter
 */

declare( strict_types=1 );
namespace DonationRouter;

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

/**
 * Handles form submissions and AJAX requests for country routing rules.
 *
 * All write actions use admin-post.php with nonce + capability checks.
 * AJAX endpoints return JSON and are restricted to logged-in users with
 * the manage_give_settings capability.
 *
 * @since 1.0.0
 */
class Country_Routing_Handler {

	/**
	 * Admin-post action for saving a routing rule (new or existing).
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	public const ACTION_SAVE = 'ador_save_routing';

	/**
	 * Admin-post action for deactivating a single routing rule.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	public const ACTION_DEACTIVATE = 'ador_deactivate_routing';

	/**
	 * Admin-post action for bulk-deactivating routing rules.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	public const ACTION_BULK_DEACTIVATE = 'ador_bulk_deactivate_routings';

	/**
	 * AJAX action for retrieving GiveWP donation forms.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	public const AJAX_GET_FORMS = 'ador_get_forms';

	/**
	 * AJAX action for retrieving active PayPal accounts.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	public const AJAX_GET_ACCOUNTS = 'ador_get_accounts';

	/**
	 * Routing data source.
	 *
	 * @since 1.0.0
	 *
	 * @var Country_Routing_Repository_Interface
	 */
	private Country_Routing_Repository_Interface $routing_repo;

	/**
	 * Account data source (for AJAX accounts endpoint and form lookups).
	 *
	 * @since 1.0.0
	 *
	 * @var Account_Repository_Interface
	 */
	private Account_Repository_Interface $account_repo;

	/**
	 * Input validator.
	 *
	 * @since 1.0.0
	 *
	 * @var Routing_Validator
	 */
	private Routing_Validator $validator;

	/**
	 * Initialise with required dependencies.
	 *
	 * @since 1.0.0
	 *
	 * @param Country_Routing_Repository_Interface $routing_repo  Routing data source.
	 * @param Account_Repository_Interface         $account_repo  Account data source.
	 */
	public function __construct(
		Country_Routing_Repository_Interface $routing_repo,
		Account_Repository_Interface $account_repo
	) {
		$this->routing_repo = $routing_repo;
		$this->account_repo = $account_repo;
		$this->validator    = new Routing_Validator();
	}

	/**
	 * Register WordPress hooks for all actions and AJAX endpoints.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function register_hooks(): void {
		add_action( 'admin_post_' . self::ACTION_SAVE, array( $this, 'handle_save_routing' ) );
		add_action( 'admin_post_' . self::ACTION_DEACTIVATE, array( $this, 'handle_deactivate_routing' ) );
		add_action( 'admin_post_' . self::ACTION_BULK_DEACTIVATE, array( $this, 'handle_bulk_deactivate' ) );
		add_action( 'wp_ajax_' . self::AJAX_GET_FORMS, array( $this, 'ajax_get_forms' ) );
		add_action( 'wp_ajax_' . self::AJAX_GET_ACCOUNTS, array( $this, 'ajax_get_accounts' ) );
	}

	/**
	 * Handle save routing rule form submission (new or update).
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function handle_save_routing(): void {
		if ( ! current_user_can( Admin_Settings::CAPABILITY ) ) {
			wp_die( esc_html__( 'You do not have permission to perform this action.', 'alamin-donation-router' ) );
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Missing
		$routing_id_raw = isset( $_POST['routing_id'] ) ? absint( $_POST['routing_id'] ) : null;
		$routing_id     = ( null !== $routing_id_raw && $routing_id_raw > 0 ) ? $routing_id_raw : null;

		$nonce_action = null !== $routing_id
			? 'ador_save_routing_' . $routing_id
			: 'ador_save_routing_new';

		check_admin_referer( $nonce_action, 'ador_nonce' );

		try {
			// phpcs:ignore WordPress.Security.NonceVerification.Missing
			$fields = $this->validator->validate( wp_unslash( (array) $_POST ) );
		} catch ( \InvalidArgumentException $e ) {
			$this->set_notice( 'error', $e->getMessage() );
			wp_safe_redirect( $this->get_routing_redirect_url( $routing_id ) );
			exit;
		}

		// Enforce one active routing per country.
		$conflict = $this->routing_repo->find_active_by_country_excluding(
			$fields['country_code'],
			$routing_id
		);

		if ( null !== $conflict ) {
			$this->set_notice(
				'error',
				sprintf(
					/* translators: %s: country code */
					__( 'An active routing rule already exists for country "%s". Deactivate it first.', 'alamin-donation-router' ),
					esc_html( $fields['country_code'] )
				)
			);
			wp_safe_redirect( $this->get_routing_redirect_url( $routing_id ) );
			exit;
		}

		try {
			$routing = new Country_Routing(
				id: $routing_id,
				country_code: $fields['country_code'],
				form_id: $fields['form_id'],
				account_id: $fields['account_id'],
				display_label: $fields['display_label'],
				display_description: '' !== $fields['display_description'] ? $fields['display_description'] : null,
				sort_order: $fields['sort_order'],
				is_active: $fields['is_active'],
				created_at: new \DateTimeImmutable(),
				updated_at: new \DateTimeImmutable(),
			);

			$saved_id = $this->routing_repo->save( $routing );

			$this->set_notice( 'success', __( 'Routing rule saved successfully.', 'alamin-donation-router' ) );
			wp_safe_redirect( $this->get_edit_routing_url( $saved_id ) );
			exit;
		} catch ( \InvalidArgumentException | Persistence_Exception $e ) {
			$this->set_notice( 'error', $e->getMessage() );
			wp_safe_redirect( $this->get_routing_redirect_url( $routing_id ) );
			exit;
		}
	}

	/**
	 * Handle deactivate routing rule form action.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function handle_deactivate_routing(): void {
		if ( ! current_user_can( Admin_Settings::CAPABILITY ) ) {
			wp_die( esc_html__( 'You do not have permission to perform this action.', 'alamin-donation-router' ) );
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$routing_id = isset( $_GET['routing_id'] ) ? absint( $_GET['routing_id'] ) : 0;

		if ( $routing_id <= 0 ) {
			wp_die( esc_html__( 'Invalid routing rule ID.', 'alamin-donation-router' ) );
		}

		check_admin_referer( 'ador_deactivate_routing_' . $routing_id, 'ador_nonce' );

		$deactivated = $this->routing_repo->deactivate( $routing_id );

		$this->set_notice(
			$deactivated ? 'success' : 'error',
			$deactivated
				? __( 'Routing rule deactivated.', 'alamin-donation-router' )
				: __( 'Routing rule could not be deactivated.', 'alamin-donation-router' )
		);

		wp_safe_redirect( $this->get_routing_list_url() );
		exit;
	}

	/**
	 * Handle bulk deactivate routing rules form action.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function handle_bulk_deactivate(): void {
		if ( ! current_user_can( Admin_Settings::CAPABILITY ) ) {
			wp_die( esc_html__( 'You do not have permission to perform this action.', 'alamin-donation-router' ) );
		}

		check_admin_referer( 'ador_bulk_deactivate_routings', 'ador_bulk_nonce' );

		// phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		$raw_ids = isset( $_POST['routing_ids'] ) && is_array( $_POST['routing_ids'] )
			? array_map( 'absint', wp_unslash( (array) $_POST['routing_ids'] ) ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
			: array();

		$count = 0;
		foreach ( $raw_ids as $routing_id ) {
			if ( $routing_id > 0 && $this->routing_repo->deactivate( $routing_id ) ) {
				++$count;
			}
		}

		$this->set_notice(
			'success',
			sprintf(
				/* translators: %d: number of rules deactivated */
				_n(
					'%d routing rule deactivated.',
					'%d routing rules deactivated.',
					$count,
					'alamin-donation-router'
				),
				$count
			)
		);

		wp_safe_redirect( $this->get_routing_list_url() );
		exit;
	}

	/**
	 * AJAX handler: return all give_forms posts as JSON.
	 *
	 * Response: array of {id: int, title: string}
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function ajax_get_forms(): void {
		if ( ! current_user_can( Admin_Settings::CAPABILITY ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions.', 'alamin-donation-router' ) ), 403 );
		}

		check_ajax_referer( 'ador_admin_js', 'nonce' );

		$posts = get_posts(
			array(
				'post_type'      => 'give_forms',
				'post_status'    => 'publish',
				'posts_per_page' => -1,
				'orderby'        => 'title',
				'order'          => 'ASC',
			)
		);

		$forms = array();
		foreach ( $posts as $post ) {
			$forms[] = array(
				'id'    => (int) $post->ID,
				'title' => (string) $post->post_title,
			);
		}

		wp_send_json_success( $forms );
	}

	/**
	 * AJAX handler: return all active PayPal accounts as JSON.
	 *
	 * Credentials are never included in the response.
	 * Response: array of {id: int, label: string, country: string}
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function ajax_get_accounts(): void {
		if ( ! current_user_can( Admin_Settings::CAPABILITY ) ) {
			wp_send_json_error( array( 'message' => __( 'Insufficient permissions.', 'alamin-donation-router' ) ), 403 );
		}

		check_ajax_referer( 'ador_admin_js', 'nonce' );

		$all_accounts = $this->account_repo->find_all_active();

		$accounts = array();
		foreach ( $all_accounts as $account ) {
			$accounts[] = array(
				'id'      => (int) $account->id,
				'label'   => (string) $account->account_label,
				'country' => (string) $account->account_country,
			);
		}

		wp_send_json_success( $accounts );
	}

	/**
	 * Store an admin notice in a per-user transient for display after redirect.
	 *
	 * @since 1.0.0
	 *
	 * @param string $type     Notice type: 'success', 'error', or 'warning'.
	 * @param string $message  Notice message text.
	 *
	 * @return void
	 */
	private function set_notice( string $type, string $message ): void {
		set_transient(
			'ador_admin_notice_' . get_current_user_id(),
			array(
				'type'    => $type,
				'message' => $message,
			),
			60
		);
	}

	/**
	 * Build the routing list URL.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	private function get_routing_list_url(): string {
		return add_query_arg(
			array(
				'post_type' => 'give_forms',
				'page'      => Admin_Settings::MENU_SLUG,
				'tab'       => Admin_Settings::TAB_ROUTING,
			),
			admin_url( 'edit.php' )
		);
	}

	/**
	 * Build the edit routing URL for a given ID.
	 *
	 * @since 1.0.0
	 *
	 * @param int $routing_id  Routing rule database ID.
	 *
	 * @return string
	 */
	private function get_edit_routing_url( int $routing_id ): string {
		return add_query_arg(
			array(
				'post_type'  => 'give_forms',
				'page'       => Admin_Settings::MENU_SLUG,
				'tab'        => Admin_Settings::TAB_ROUTING,
				'view'       => 'edit',
				'routing_id' => $routing_id,
			),
			admin_url( 'edit.php' )
		);
	}

	/**
	 * Build a redirect URL appropriate for the current save context.
	 *
	 * @since 1.0.0
	 *
	 * @param int|null $routing_id  Existing routing ID (edit mode) or null (add mode).
	 *
	 * @return string
	 */
	private function get_routing_redirect_url( ?int $routing_id ): string {
		if ( null !== $routing_id ) {
			return $this->get_edit_routing_url( $routing_id );
		}

		return add_query_arg(
			array(
				'post_type' => 'give_forms',
				'page'      => Admin_Settings::MENU_SLUG,
				'tab'       => Admin_Settings::TAB_ROUTING,
				'view'      => 'add',
			),
			admin_url( 'edit.php' )
		);
	}
}
