<?php
/**
 * Admin submenu page for managing PayPal accounts.
 *
 * @since   1.0.0
 * @package DonationRouter
 */

declare( strict_types=1 );
namespace DonationRouter;

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

/**
 * Registers and renders the plugin submenu under Donation Forms.
 *
 * Tabs: Accounts, Country Routing, Settings (placeholder).
 *
 * @since 1.0.0
 */
class Admin_Settings {

	use Template_Loader;

	/**
	 * Submenu page slug.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	public const MENU_SLUG = 'alamin-donation-router';

	/**
	 * Required WordPress capability to access this page.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	public const CAPABILITY = 'manage_give_settings';

	/**
	 * Accounts tab slug.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	public const TAB_ACCOUNTS = 'accounts';

	/**
	 * Country Routing tab slug.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	public const TAB_ROUTING = 'country-routing';

	/**
	 * Settings tab slug.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	public const TAB_SETTINGS = 'settings';

	/**
	 * Hook suffix returned by add_submenu_page().
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	private string $hook_suffix = '';

	/**
	 * Account data source.
	 *
	 * @since 1.0.0
	 *
	 * @var Account_Repository_Interface
	 */
	private Account_Repository_Interface $account_repo;

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

	/**
	 * Onboarding / form-action handler.
	 *
	 * @since 1.0.0
	 *
	 * @var Onboarding_Handler
	 */
	private Onboarding_Handler $onboarding; // @phpstan-ignore-line

	/**
	 * Settings tab renderer and save handler.
	 *
	 * @since 1.0.0
	 *
	 * @var Plugin_Settings
	 */
	private Plugin_Settings $plugin_settings;

	/**
	 * Readiness checks for the setup panel.
	 *
	 * @since 1.2.0
	 *
	 * @var Setup_Status
	 */
	private Setup_Status $setup_status;

	/**
	 * Initialise with required dependencies.
	 *
	 * @since 1.0.0
	 *
	 * @param Account_Repository_Interface         $account_repo Account data source.
	 * @param Onboarding_Handler                   $onboarding   PayPal onboarding and form handler.
	 * @param Country_Routing_Repository_Interface $routing_repo Routing data source.
	 * @param Setup_Status|null                    $setup_status Optional readiness checker (injected in tests).
	 */
	public function __construct(
		Account_Repository_Interface $account_repo,
		Onboarding_Handler $onboarding,
		Country_Routing_Repository_Interface $routing_repo,
		?Setup_Status $setup_status = null
	) {
		$this->account_repo    = $account_repo;
		$this->onboarding      = $onboarding;
		$this->routing_repo    = $routing_repo;
		$this->plugin_settings = new Plugin_Settings();
		$this->setup_status    = $setup_status ?? new Setup_Status( $account_repo, $routing_repo );
	}

	/**
	 * Register WordPress hooks for this class.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function register_hooks(): void {
		add_action( 'admin_menu', array( $this, 'register_submenu' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
		add_action( 'admin_notices', array( $this, 'maybe_render_notice' ) );
		$this->plugin_settings->register_hooks();
	}

	/**
	 * Add the plugin submenu under the Give Forms post type.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function register_submenu(): void {
		$hook = add_submenu_page(
			'edit.php?post_type=give_forms',
			__( 'Donation Router', 'alamin-donation-router' ),
			__( 'PayPal Accounts', 'alamin-donation-router' ),
			self::CAPABILITY,
			self::MENU_SLUG,
			array( $this, 'render_page' )
		);

		if ( is_string( $hook ) ) {
			$this->hook_suffix = $hook;

			add_action( 'load-' . $hook, array( $this, 'register_help_tabs' ) );
		}
	}

	/**
	 * Register WordPress contextual help for the plugin screen.
	 *
	 * @since 1.2.0
	 *
	 * @return void
	 */
	public function register_help_tabs(): void {
		$screen = get_current_screen();

		if ( ! $screen ) {
			return;
		}

		$screen->add_help_tab(
			array(
				'id'      => 'ador-setup-order',
				'title'   => __( 'Setup order', 'alamin-donation-router' ),
				'content' => '<p>' . esc_html__( 'Add a PayPal account, add a country routing rule pointing at it, place the [ador_country_selector] shortcode on your donation page, then register each account\'s webhook URL in PayPal and paste the webhook ID back.', 'alamin-donation-router' ) . '</p>'
					. '<p>' . esc_html__( 'Donations from a country with no active rule are not blocked — they fall through to GiveWP\'s own default PayPal account.', 'alamin-donation-router' ) . '</p>',
			)
		);

		$screen->add_help_tab(
			array(
				'id'      => 'ador-routing',
				'title'   => __( 'Country routing', 'alamin-donation-router' ),
				'content' => '<p>' . esc_html__( 'Each country may have one active rule. A rule chooses both the donation form the donor sees and the PayPal account that receives the money.', 'alamin-donation-router' ) . '</p>',
			)
		);

		$screen->add_help_tab(
			array(
				'id'      => 'ador-receipts',
				'title'   => __( 'Receipts', 'alamin-donation-router' ),
				'content' => '<p>' . esc_html__( 'Seven email tags resolve to the legal entity that received each donation — its name, address, tax ID, country, tax-deductibility statement, the donor\'s country, and a per-entity receipt number. They return an empty string for donations this plugin did not route.', 'alamin-donation-router' ) . '</p>',
			)
		);

		$screen->set_help_sidebar(
			'<p><strong>' . esc_html__( 'More', 'alamin-donation-router' ) . '</strong></p>'
			. '<p><a href="https://github.com/mralaminahamed/donation-router-for-givewp-paypal-donations/wiki" target="_blank" rel="noopener noreferrer">' . esc_html__( 'Documentation', 'alamin-donation-router' ) . '</a></p>'
		);
	}

	/**
	 * Enqueue plugin CSS and JS only on this plugin's admin page.
	 *
	 * @since 1.0.0
	 *
	 * @param string $hook_suffix Current admin page hook suffix.
	 *
	 * @return void
	 */
	public function enqueue_assets( string $hook_suffix ): void {
		if ( $this->hook_suffix !== $hook_suffix ) {
			return;
		}

		$asset_file = ADOR_PATH . 'build/admin-settings.asset.php';
		$asset      = file_exists( $asset_file )
			? require $asset_file
			: array(
				'dependencies' => array(),
				'version'      => ADOR_VERSION,
			);

		wp_enqueue_style(
			'ador-admin',
			ADOR_URL . 'build/admin-settings.css',
			array(),
			$asset['version']
		);

		wp_enqueue_script(
			'ador-admin',
			ADOR_URL . 'build/admin-settings.js',
			array_merge( array( 'jquery' ), $asset['dependencies'] ),
			$asset['version'],
			true
		);

		wp_localize_script(
			'ador-admin',
			'adorAdmin',
			array(
				'nonce'                   => wp_create_nonce( 'ador_admin_js' ),
				'confirmDeactivate'       => __( 'Are you sure you want to deactivate this account? Donations will no longer be routed to it.', 'alamin-donation-router' ),
				'confirmBulk'             => __( 'Are you sure you want to deactivate the selected accounts?', 'alamin-donation-router' ),
				'confirmResync'           => __( 'Re-sync this account’s credentials from GiveWP? This overwrites the stored credentials.', 'alamin-donation-router' ),
				'confirmDelete'           => __( 'Delete this PayPal account permanently? This cannot be undone. Its stored credentials are removed and any registered webhook is deleted at PayPal.', 'alamin-donation-router' ),
				'copyLabel'               => __( 'Copy', 'alamin-donation-router' ),
				'copiedLabel'             => __( 'Copied!', 'alamin-donation-router' ),
				'copyIcon'                => ador_icon( 'copy' ),
				'checkIcon'               => ador_icon( 'check' ),

				// Multi-account "Connect with PayPal" flow (add-account page).
				'ajaxUrl'                 => admin_url( 'admin-ajax.php' ),
				'connectNonce'            => wp_create_nonce( PayPal_Connect_Handler::NONCE_ACTION ),
				'connectPartnerAction'    => PayPal_Connect_Handler::ACTION_PARTNER_LINK,
				'connectStoreCodesAction' => PayPal_Connect_Handler::ACTION_STORE_CODES,
				'connectStarting'         => __( 'Contacting PayPal…', 'alamin-donation-router' ),
				'connectPopup'            => __( 'Complete onboarding in the PayPal window…', 'alamin-donation-router' ),
				'connectFinishing'        => __( 'Finishing…', 'alamin-donation-router' ),
				'connectContinue'         => __( 'Continue to PayPal →', 'alamin-donation-router' ),
				'connectArmed'            => __( 'Ready — click “Continue to PayPal” to open the PayPal window.', 'alamin-donation-router' ),
			)
		);

		// Enqueue PayPal's partner onboarding SDK the same way GiveWP core does —
		// as a real server-rendered element rather than a dynamically injected one,
		// which is what lets its onboarding callback register reliably. Loaded from
		// the live host even in sandbox, since the sandbox host is not ramped for
		// the onboarding lightbox. A blank version stops WordPress adding a query
		// string that would break the PayPal URL.
		wp_enqueue_script(
			'ador-paypal-partner-sdk',
			'https://www.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js',
			array(),
			null, // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion -- external PayPal SDK; a ?ver query would break the URL.
			true
		);
	}

	/**
	 * Render an admin notice stored by form handlers via transient.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function maybe_render_notice(): void {
		$screen = get_current_screen();
		if ( ! $screen || $this->hook_suffix !== $screen->id ) {
			return;
		}

		$notice = get_transient( 'ador_admin_notice_' . get_current_user_id() );
		if ( ! is_array( $notice ) ) {
			return;
		}

		delete_transient( 'ador_admin_notice_' . get_current_user_id() );

		$type    = in_array( (string) ( $notice['type'] ?? '' ), array( 'success', 'error', 'warning' ), true )
		? (string) $notice['type']
		: 'info';
		$message = isset( $notice['message'] ) ? (string) $notice['message'] : '';

		if ( '' === $message ) {
			return;
		}

		printf(
			'<div class="notice notice-%s is-dismissible"><p>%s</p></div>',
			esc_attr( $type ),
			esc_html( $message )
		);
	}

	/**
	 * Return the currently active tab, validated against the allowed set.
	 *
	 * @since 1.0.0
	 *
	 * @return string
	 */
	private function get_current_tab(): string {
     // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$raw = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( (string) $_GET['tab'] ) ) : self::TAB_ACCOUNTS;

		return in_array( $raw, array( self::TAB_ACCOUNTS, self::TAB_ROUTING, self::TAB_SETTINGS ), true )
		? $raw
		: self::TAB_ACCOUNTS;
	}

	/**
	 * Render the full admin page including tab navigation.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function render_page(): void {
		if ( ! current_user_can( self::CAPABILITY ) ) {
			wp_die( esc_html__( 'You do not have permission to access this page.', 'alamin-donation-router' ) );
		}

		$current_tab = $this->get_current_tab();

		$tab_definitions = array(
			array(
				'tab'   => self::TAB_ACCOUNTS,
				'label' => __( 'Accounts', 'alamin-donation-router' ),
			),
			array(
				'tab'   => self::TAB_ROUTING,
				'label' => __( 'Country Routing', 'alamin-donation-router' ),
			),
			array(
				'tab'   => self::TAB_SETTINGS,
				'label' => __( 'Settings', 'alamin-donation-router' ),
			),
		);

		$tab_links = array();
		foreach ( $tab_definitions as $def ) {
			$tab_links[] = array(
				'url'   => add_query_arg(
					array(
						'post_type' => 'give_forms',
						'page'      => self::MENU_SLUG,
						'tab'       => $def['tab'],
					),
					admin_url( 'edit.php' )
				),
				'label' => $def['label'],
				'class' => $def['tab'] === $current_tab ? 'nav-tab nav-tab-active' : 'nav-tab',
			);
		}

		$render_content = function () use ( $current_tab ): void {
			match ( $current_tab ) {
				self::TAB_ACCOUNTS => $this->render_accounts_tab(),
				self::TAB_ROUTING  => $this->render_routing_tab(),
				self::TAB_SETTINGS => $this->render_settings_tab(),
				default            => $this->render_accounts_tab(),
			};
		};

		$this->render_template( 'admin/admin-settings-page.php', compact( 'tab_links', 'render_content' ) );
	}

	/**
	 * Render the Accounts tab — list view or add/edit form.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	private function render_accounts_tab(): void {
     // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$view = isset( $_GET['view'] ) ? sanitize_key( wp_unslash( (string) $_GET['view'] ) ) : 'list';
     // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$account_id = isset( $_GET['account_id'] ) ? absint( $_GET['account_id'] ) : null;

		if ( in_array( $view, array( 'add', 'edit' ), true ) ) {
			$form = new Account_Form( $this->account_repo );
			$form->render( $account_id );
		} elseif ( 'webhook' === $view && null !== $account_id ) {
			$this->render_webhook_view( $account_id );
		} else {
			$this->render_list_view();
		}
	}

	/**
	 * Render the Webhook Setup view for a single account.
	 *
	 * Displays the unique per-account webhook URL the operator must register
	 * in the PayPal developer dashboard, along with the required event types.
	 *
	 * @since 1.0.0
	 *
	 * @param int $account_id  Account post ID.
	 *
	 * @return void
	 */
	private function render_webhook_view( int $account_id ): void {
		try {
			$account = $this->account_repo->find_by_id( $account_id );
		} catch ( \Throwable $e ) {
			echo '<div class="notice notice-error"><p>' . esc_html__( 'Account not found.', 'alamin-donation-router' ) . '</p></div>';
			return;
		}

		$webhook_url = Webhook_Router::get_webhook_url( $account_id );
		$back_url    = add_query_arg(
			array(
				'post_type' => 'give_forms',
				'page'      => self::MENU_SLUG,
				'tab'       => self::TAB_ACCOUNTS,
			),
			admin_url( 'edit.php' )
		);
		$event_types = array(
			'PAYMENT.CAPTURE.COMPLETED',
			'PAYMENT.CAPTURE.REFUNDED',
			'PAYMENT.CAPTURE.DENIED',
			'CUSTOMER.MERCHANT-INTEGRATION.PRODUCT-SUBSCRIPTION-UPDATED',
		);

		$this->render_template( 'admin/admin-settings-webhook.php', compact( 'account', 'webhook_url', 'back_url', 'event_types' ) );
	}

	/**
	 * Render the accounts list view with list table and Add New button.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	private function render_list_view(): void {
		$this->render_template(
			'admin/setup-status.php',
			array(
				'checks'     => $this->setup_status->checks(),
				'is_ready'   => $this->setup_status->is_ready(),
				'fail_count' => $this->setup_status->count_of( Setup_Check::FAIL ),
				'warn_count' => $this->setup_status->count_of( Setup_Check::WARN ),
			)
		);

		$add_url = add_query_arg(
			array(
				'post_type' => 'give_forms',
				'page'      => self::MENU_SLUG,
				'tab'       => self::TAB_ACCOUNTS,
				'view'      => 'add',
			),
			admin_url( 'edit.php' )
		);

		$list_table = new Account_List_Table( $this->account_repo );
		$list_table->prepare_items();

		$import = $this->import_view_state( new GiveWP_Merchant_Reader() );

		$this->render_template( 'admin/admin-settings-accounts-list.php', compact( 'add_url', 'list_table', 'import' ) );
	}

	/**
	 * Whether GiveWP has a connected PayPal account that is not yet managed by
	 * this plugin — the precondition for a usable "import" action.
	 *
	 * @since 1.1.0
	 *
	 * @param GiveWP_Merchant_Reader|null $reader Optional reader (injected in tests).
	 *
	 * @return bool
	 */
	public function can_import_givewp_account( ?GiveWP_Merchant_Reader $reader = null ): bool {
		return ! empty( $this->import_view_state( $reader ?? new GiveWP_Merchant_Reader() )['available'] );
	}

	/**
	 * Resolve the state of the "Import from GiveWP" affordance for the accounts
	 * list. The button is always rendered for discoverability; when importing is
	 * not currently possible it carries a `hint` explaining why so the template
	 * can show it disabled.
	 *
	 * @since 1.1.0
	 *
	 * @param GiveWP_Merchant_Reader $reader  GiveWP merchant reader.
	 *
	 * @return array{available:bool,hint?:string,action?:string,nonce_action?:string,merchant_id?:string,country?:string,mode?:string}
	 */
	private function import_view_state( GiveWP_Merchant_Reader $reader ): array {
		if ( ! $reader->is_connected() || '' === $reader->merchant_id() ) {
			return array(
				'available' => false,
				'hint'      => __( 'Connect a PayPal account in GiveWP (Settings → Payment Gateways → PayPal Donations) to import it here.', 'alamin-donation-router' ),
			);
		}

		$merchant_id = $reader->merchant_id();
		$mode        = $reader->current_mode();

		try {
			$this->account_repo->find_by_merchant_id_and_mode( $merchant_id, $mode );

			// GiveWP's connected account is already managed for this environment.
			return array(
				'available' => false,
				'hint'      => __( 'GiveWP’s connected PayPal account is already imported.', 'alamin-donation-router' ),
			);
		} catch ( Account_Not_Found_Exception $e ) {
			return array(
				'available'    => true,
				'action'       => Onboarding_Handler::ACTION_IMPORT,
				'nonce_action' => Onboarding_Handler::ACTION_IMPORT,
				'merchant_id'  => $merchant_id,
				'country'      => $reader->account_country(),
				'mode'         => $mode,
			);
		}
	}

	/**
	 * Render the Country Routing tab — list view or add/edit form.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	private function render_routing_tab(): void {
     // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$view = isset( $_GET['view'] ) ? sanitize_key( wp_unslash( (string) $_GET['view'] ) ) : 'list';
     // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$routing_id = isset( $_GET['routing_id'] ) ? absint( $_GET['routing_id'] ) : null;

		if ( in_array( $view, array( 'add', 'edit' ), true ) ) {
			$form = new Country_Routing_Form( $this->routing_repo, $this->account_repo );
			$form->render( 'add' === $view ? null : $routing_id );
		} else {
			$this->render_routing_list_view();
		}
	}

	/**
	 * Render the routing list view with the list table and Add New button.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	private function render_routing_list_view(): void {
		$add_url = add_query_arg(
			array(
				'post_type' => 'give_forms',
				'page'      => self::MENU_SLUG,
				'tab'       => self::TAB_ROUTING,
				'view'      => 'add',
			),
			admin_url( 'edit.php' )
		);

		$list_table = new Country_Routing_List_Table( $this->routing_repo, $this->account_repo );
		$list_table->prepare_items();

		$this->render_template( 'admin/admin-settings-routing-list.php', compact( 'add_url', 'list_table' ) );
	}

	/**
	 * Render the Settings tab placeholder.
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	private function render_settings_tab(): void {
		$this->plugin_settings->render();
	}
}
