<?php
/**
 * Setup admin controller for plugin setup wizard.
 *
 * @package Awesomemotive\Rewardswp\Controllers\Admin
 * @since 1.0.0
 */

namespace Awesomemotive\Rewardswp\Controllers\Admin;

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

use Awesomemotive\Rewardswp\Factories\ViewsFactory;
use Awesomemotive\Rewardswp\Services\OptionsService;
use Awesomemotive\Rewardswp\Interfaces\ControllerInterface;
use Awesomemotive\Rewardswp\Services\SettingsScreenService;
use Awesomemotive\Rewardswp\Services\LicenseManagerService;
use Defuse\Crypto\Crypto;
use Defuse\Crypto\Key;
use function Awesomemotive\Rewardswp\Helpers\get_rewardswp_plan_tier;
use function Awesomemotive\Rewardswp\Helpers\get_rewardswp_setup_template;

class SetupAdminController implements ControllerInterface {
	/**
	 * License manager service instance.
	 *
	 * @since 1.1.0
	 *
	 * @var LicenseManagerService
	 */
	private LicenseManagerService $license_manager_service;

	/**
	 * Options service instance.
	 *
	 * @since 1.1.0
	 *
	 * @var OptionsService
	 */
	private OptionsService $options_service;

	/**
	 * Results URL
	 *
	 * This is the URL to the Laravel Application
	 * where we can get results of a setup.
	 *
	 * @since 1.0.0
	 *
	 * @var string
	 */
	private string $results_url;

	/**
	 * Construct.
	 *
	 * @since 1.0.0
	 *
	 * @param ViewsFactory          $views_factory
	 * @param SettingsScreenService $settings_screen_service
	 * @param LicenseManagerService $license_manager_service
	 * @param OptionsService        $options_service
	 */
	public function __construct( ViewsFactory $views_factory, SettingsScreenService $settings_screen_service, LicenseManagerService $license_manager_service, OptionsService $options_service ) {

		$this->license_manager_service = $license_manager_service;
		$this->options_service         = $options_service;

		// Use get_rewardswp_setup_template() which maps basic/plus/pro to 'pro'.
		$this->results_url = sprintf(
			defined( 'REWARDSWP_LARAVEL_APP_REWARDSWP_SETUP_URL' )
				? trailingslashit( untrailingslashit( REWARDSWP_LARAVEL_APP_REWARDSWP_SETUP_URL ) . '/results' )
				: 'https://app.rewardswp.com/setup/%s/results/',
			strtolower( get_rewardswp_setup_template() )
		);
	}

	/**
	 * Init (hooks).
	 *
	 * @since 1.0.0
	 *
	 * @return void
	 */
	public function init() {
		add_action( 'admin_init', [ $this, 'maybe_migrate_setup_data' ] );
	}

	/**
	 * Migrate setup data (Settings) to RewardsWP.
	 *
	 * @since 1.0.0
	 */
	public function maybe_migrate_setup_data(): void {

		$plan_tier = get_rewardswp_plan_tier();

		if ( 'complete' !== filter_input( INPUT_GET, 'setup', FILTER_UNSAFE_RAW ) ) {
			return;
		}

		if ( 'rewardswp' !== filter_input( INPUT_GET, 'page', FILTER_UNSAFE_RAW ) ) {
			return;
		}

		$referrer_secret = get_option( sprintf( 'rewardswp_%s_setup_referrer_secret', get_rewardswp_setup_template() ), '' );

		if ( empty( $referrer_secret ) || ! is_string( $referrer_secret ) ) {
			return;
		}

		$response = wp_remote_get(
			add_query_arg(
				[
					'referrer_secret' => $referrer_secret,
					'referrer'        => Crypto::encrypt(
						admin_url( 'admin.php?page=rewardswp' ),
						Key::loadFromAsciiSafeString( $referrer_secret )
					),
				],
				$this->results_url
			)
		);

		if ( is_wp_error( $response ) ) {
			return;
		}

		$response_code = wp_remote_retrieve_response_code( $response );

		if ( 200 !== $response_code ) {
			return;
		}

		$body    = wp_remote_retrieve_body( $response );
		$results = json_decode( $body, true );

		// Check if JSON decoding was successful.
		if ( json_last_error() !== JSON_ERROR_NONE ) {
			return;
		}

		if ( ! is_array( $results ) ) {
			return;
		}

		// Store the raw results.
		update_option(
			sprintf( 'rewardswp_%s_setup_results', get_rewardswp_setup_template() ),
			$results,
			false
		);

		// Get incoming settings from wizard results.
		$incoming_settings = ( isset( $results['settings'] ) && is_array( $results['settings'] ) )
			? $results['settings']
			: [];

		// Get current settings from OptionsService (uses its internal cache).
		$current_settings = $this->options_service->get_options();

		// Merge incoming settings with current settings.
		$merged_settings = array_replace_recursive( $current_settings, $incoming_settings );

		/**
		 * Update earn/redeem rewards based on wizard values.
		 * This modifies the merged_settings array directly to avoid OptionsService cache conflicts.
		 */
		$this->update_rewards_in_settings( $results, $merged_settings );

		/**
		 * Save the complete merged settings through OptionsService.
		 * This updates OptionsService's internal cache AND saves to database,
		 * preventing the destructor from overwriting with stale data.
		 */
		$this->options_service->update_options( $merged_settings );

		$deferred_actions = get_option( 'rewardswp_deferred_actions', [] );

		// Update any deferred actions from the results.
		update_option(
			'rewardswp_deferred_actions',
			array_merge(
				$deferred_actions,
				( isset( $results['deferred_actions'] ) && is_array( $results['deferred_actions'] ) )
					? $results['deferred_actions']
					: []
			),
			false
		);

		// Track when we last migrated the setup data.
		update_option( sprintf( 'rewardswp_%s_setup_migrated', get_rewardswp_setup_template() ), time(), false );

		// Activate license if provided in wizard results.
		$license_key = $results['settings']['general']['license']['key'] ?? null;
		if ( ! empty( $license_key ) ) {
			$this->license_manager_service->activate_license( $license_key );
		}

		// Remove the ?setup=complete query parameter.
		wp_safe_redirect( remove_query_arg( 'setup' ) );
		exit;
	}

	/**
	 * Update earn/spend rewards in the settings array based on wizard values.
	 *
	 * Modifies the settings array directly to update:
	 * - Purchase earn action's points_per_dollar value
	 * - Redeem action points_required based on currency_per_point
	 *
	 * @since 1.1.0
	 *
	 * @param array $results  The wizard results containing points settings.
	 * @param array $settings The settings array to modify (passed by reference).
	 * @return void
	 */
	private function update_rewards_in_settings( array $results, array &$settings ): void {
		// Extract wizard values.
		$points_per_currency_unit = $results['settings']['points']['points_per_currency_unit'] ?? null;
		$currency_per_point       = $results['settings']['points']['currency_per_point'] ?? null;

		// Update purchase earn action if points_per_currency_unit provided.
		if ( null !== $points_per_currency_unit && isset( $settings['points']['earn_actions']['purchase'] ) ) {
			$settings['points']['earn_actions']['purchase']['points_per_dollar'] = (int) $points_per_currency_unit;
		}

		// Update redeem actions if currency_per_point provided.
		if ( null !== $currency_per_point && $currency_per_point > 0 && isset( $settings['points']['spend_actions'] ) ) {
			foreach ( $settings['points']['spend_actions'] as $key => $action ) {
				// Skip actions without coupon_amount (e.g., free shipping).
				if ( ! isset( $action['coupon_amount'] ) || $action['coupon_amount'] <= 0 ) {
					continue;
				}
				// Recalculate points_required based on coupon value.
				$settings['points']['spend_actions'][ $key ]['points_required'] = (int) round( $action['coupon_amount'] / $currency_per_point );
			}
		}
	}
}
