<?php

namespace SugarCalendarBookings\SetupWizard;

use SugarCalendarBookings\Locations\LocationService;
use SugarCalendarBookings\Migration\SetupWizardMigrationHandler;
use SugarCalendarBookings\Options;
use SugarCalendarBookings\Plugin;
use SugarCalendarBookings\Pro\License\License;
use SugarCalendarBookings\Support\License\Connect;
use SugarCalendarBookings\Support\License\LicenseApiService;
use SugarCalendarBookings\Vendor\ProductApi\Auth\SiteRegistration;
use SugarCalendarBookings\Vendor\ProductApi\ProductApi;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * Class RestApi.
 *
 * REST API endpoints for the Setup Wizard.
 *
 * @since 1.2.0
 */
class RestApi {

	/**
	 * URL prefix.
	 *
	 * @since 1.2.0
	 *
	 * @var string
	 */
	const URL_PREFIX = 'sugar-calendar-bookings/v1/setup-wizard';

	/**
	 * Setup wizard service.
	 *
	 * @since 1.2.0
	 *
	 * @var SetupWizardService
	 */
	private $service;

	/**
	 * Constructor.
	 *
	 * @since 1.2.0
	 */
	public function __construct() {

		$this->service = Plugin::get( SetupWizardService::class );
	}

	/**
	 * Get the API URL.
	 *
	 * @since 1.2.0
	 *
	 * @return string
	 */
	public function get_url() {

		return get_rest_url( null, self::URL_PREFIX );
	}

	/**
	 * Register routes.
	 *
	 * @since 1.2.0
	 */
	public function register_routes() {

		$routes = [
			'/hydrate'                 => [
				WP_REST_Server::READABLE,
				[ $this, 'hydrate' ],
			],
			'/update'                  => [
				WP_REST_Server::CREATABLE,
				[ $this, 'update' ],
			],
			'/create-location'         => [
				WP_REST_Server::CREATABLE,
				[ $this, 'create_location' ],
			],
			'/create-service'          => [
				WP_REST_Server::CREATABLE,
				[ $this, 'create_service' ],
			],
			'/stripe-connect'          => [
				WP_REST_Server::CREATABLE,
				[ $this, 'stripe_connect' ],
			],
			'/verify-license'          => [
				WP_REST_Server::READABLE,
				[ $this, 'verify_license' ],
			],
			'/deactivate-license'      => [
				WP_REST_Server::CREATABLE,
				[ $this, 'deactivate_license' ],
			],
			'/lite-upgrade'            => [
				WP_REST_Server::CREATABLE,
				[ $this, 'lite_upgrade' ],
			],
			'/install-plugins'         => [
				WP_REST_Server::CREATABLE,
				[ $this, 'install_plugins' ],
			],

			// Migration routes.
			'/migration/start'         => [
				WP_REST_Server::CREATABLE,
				[ $this, 'migration_start' ],
			],
			'/migration/process-batch' => [
				WP_REST_Server::CREATABLE,
				[ $this, 'migration_process_batch' ],
			],
			'/migration/complete'      => [
				WP_REST_Server::CREATABLE,
				[ $this, 'migration_complete' ],
			],
		];

		foreach ( $routes as $route => $route_params ) {
			[ $method, $callback ] = $route_params;

			register_rest_route(
				self::URL_PREFIX,
				$route,
				[
					'methods'             => $method,
					'callback'            => $callback,
					'permission_callback' => [ $this, 'validate_request' ],
				]
			);
		}
	}

	/**
	 * Validate request.
	 *
	 * @since 1.2.0
	 *
	 * @param WP_REST_Request $request Current request.
	 *
	 * @return bool|WP_Error
	 */
	public function validate_request( $request ) {

		$token = $request->get_header( 'X-TOKEN' );
		$auth  = Plugin::get( Auth::class );

		$user_id = $auth->verify_token( $token );

		if ( ! $user_id ) {
			return $this->error( __( 'Session expired.', 'sugar-calendar-bookings-scheduling-appointments-lite' ), 401 );
		}

		// Hydrate the request as the bound user so capability checks
		// work even though the cross-origin wizard request carries no
		// auth cookies.
		wp_set_current_user( $user_id );

		// Re-check the capability — this catches users whose role changed
		// after the token was issued (demoted, deleted, role revoked).
		if ( ! current_user_can( 'manage_options' ) ) {
			return $this->error( __( 'Insufficient permissions.', 'sugar-calendar-bookings-scheduling-appointments-lite' ), 403 );
		}

		$auth->refresh_token();

		return true;
	}

	/**
	 * Hydrate setup wizard data.
	 *
	 * @since 1.2.0
	 *
	 * @return WP_REST_Response
	 */
	public function hydrate() {

		// Remove first run transient.
		delete_transient( SetupWizard::TRANSIENT_FIRST_RUN );

		return $this->response(
			[
				'settings'  => $this->service->get_settings(),
				'options'   => $this->service->get_options(),
				'migration' => Plugin::get( MigrationHydrator::class )->get_data(),
			]
		);
	}

	/**
	 * Update plugin options.
	 *
	 * @since 1.2.0
	 *
	 * @param WP_REST_Request $request Current request.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function update( $request ) { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh

		$data = $request->get_json_params();

		if ( empty( $data ) ) {
			return $this->response(
				[
					'settings' => $this->service->get_settings(),
					'options'  => $this->service->get_options(),
				]
			);
		}

		// Handle opt-in for usage tracking and integrations.
		if ( ! empty( $data['opt_in_tracking_and_integrations'] ) ) {

			// Explicitly enable integrations and usage tracking for Lite.
			if ( ! sugar_calendar_bookings()->is_pro() ) {
				$data['misc'] = array_merge( $data['misc'] ?? [], [ 'disable_integrations' => false ] );
			}

			// Register site with Product API for integrations.
			$registration = ProductApi::get( SiteRegistration::class );

			if ( ! $registration->is_registered() ) {
				$registration->register();
			}
		} elseif ( array_key_exists( 'opt_in_tracking_and_integrations', $data ) && ! sugar_calendar_bookings()->is_pro() ) {

			// Pro never disables integrations regardless of opt-in choice; only Lite respects the decline.
			$data['misc'] = array_merge( $data['misc'] ?? [], [ 'disable_integrations' => true ] );
		}

		unset( $data['opt_in_tracking_and_integrations'] );

		// Handle WordPress timezone separately.
		if ( isset( $data['timezone'] ) ) {
			$this->service->update_timezone( $data['timezone'] );
			unset( $data['timezone'] );
		}

		// Handle employee data separately (stored in employee table, not options).
		if ( isset( $data['employee'] ) ) {
			$this->service->update_employee( $data['employee'] );
			unset( $data['employee'] );
		}

		// Update plugin options.
		Plugin::get( Options::class )->update( $data );

		return $this->response(
			[
				'settings' => $this->service->get_settings(),
				'options'  => $this->service->get_options(),
			]
		);
	}

	/**
	 * Create location endpoint.
	 *
	 * @since 1.2.0
	 *
	 * @param WP_REST_Request $request Current request.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function create_location( $request ) {

		$data        = $request->get_json_params();
		$location_id = $this->service->create_location( $data );

		if ( is_wp_error( $location_id ) ) {
			return $this->error( $location_id->get_error_message(), 400 );
		}

		if ( ! $location_id ) {
			return $this->error(
				__( 'Location data is required.', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				400
			);
		}

		return $this->response( [ 'location_id' => $location_id ] );
	}

	/**
	 * Create service endpoint.
	 *
	 * Creates a single service with optional location assignment.
	 * If location_id is provided, uses it. Otherwise, falls back to existing location if available.
	 *
	 * @since 1.2.0
	 *
	 * @param WP_REST_Request $request Current request.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function create_service( $request ) {

		$data        = $request->get_json_params();
		$location_id = $data['location_id'] ?? Plugin::get( LocationService::class )->get_first_enabled_location_id();

		unset( $data['location_id'] );

		if ( empty( $data ) ) {
			return $this->error(
				__( 'Service data is required.', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
				400
			);
		}

		$result = $this->service->create_service( $data, $location_id );

		if ( is_wp_error( $result ) ) {
			return $this->error( $result->get_error_message(), 400 );
		}

		return $this->response( [ 'success' => true ] );
	}

	/**
	 * Perform Stripe authentication.
	 *
	 * @since 1.2.0
	 *
	 * @param WP_REST_Request $request Current request.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function stripe_connect( $request ) {

		$data = $request->get_json_params();

		if ( empty( $data['state'] ) ) {
			return $this->error( __( 'Missing required parameters.', 'sugar-calendar-bookings-scheduling-appointments-lite' ), 401 );
		}

		$result = Plugin::get( StripeSetupService::class )->connect( sanitize_text_field( $data['state'] ) );

		if ( is_wp_error( $result ) ) {
			return $this->error( $result->get_error_message(), 401 );
		}

		return $this->response( $result );
	}

	/**
	 * Verify license.
	 *
	 * @since 1.2.0
	 *
	 * @param WP_REST_Request $request Current request.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function verify_license( $request ) {

		$license_key = sanitize_text_field( $request->get_param( 'license_key' ) );

		if ( empty( $license_key ) ) {
			return $this->error( __( 'Missing license key.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		if ( ! sugar_calendar_bookings()->is_pro() ) {
			return $this->error( __( 'License verification requires Pro version.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		$license = Plugin::get( License::class );

		if ( ! $license ) {
			return $this->error( __( 'License service not available.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		if ( ! $license->verify_key( $license_key ) ) {
			$error_message = ! empty( $license->errors[0] )
				? $license->errors[0]
				: __( 'License key could not be verified.', 'sugar-calendar-bookings-scheduling-appointments-lite' );

			return $this->error( $error_message );
		}

		return $this->response(
			[
				'settings' => $this->service->get_settings(),
			]
		);
	}

	/**
	 * Deactivate license.
	 *
	 * @since 1.2.0
	 *
	 * @param WP_REST_Request $request Current request.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function deactivate_license( $request ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found

		if ( ! sugar_calendar_bookings()->is_pro() ) {
			return $this->error( __( 'License deactivation requires Pro version.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		$license = Plugin::get( License::class );

		if ( $license ) {
			$license->deactivate_key();
		}

		return $this->response();
	}

	/**
	 * Verify license key and get plugin upgrade URL.
	 *
	 * This endpoint is called from Lite version during the upgrade process.
	 * It verifies the license key with the remote API before generating the upgrade URL.
	 *
	 * Note: License data is NOT saved here. It will be saved by Connect::process()
	 * after successful Pro plugin installation, following the same pattern as SCE.
	 *
	 * @since 1.2.0
	 *
	 * @param WP_REST_Request $request Current request.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function lite_upgrade( $request ) {

		$data = $request->get_json_params();

		$license_key = isset( $data['license_key'] ) ? sanitize_text_field( $data['license_key'] ) : '';

		if ( empty( $license_key ) ) {
			return $this->error( __( 'Missing license key.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		// Check license type instead of is_pro() since Pro code can be installed with a lite license.
		$options      = Plugin::get( Options::class );
		$license_type = $options->get( 'license.type', 'lite' );

		if ( $license_type !== 'lite' ) {
			return $this->error( __( 'Only the Lite version can be upgraded.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		// Verify the license key with remote API before proceeding.
		$api_service   = Plugin::get( LicenseApiService::class );
		$verify_result = $api_service->verify_key( $license_key );

		if ( is_wp_error( $verify_result ) ) {
			return $this->error( $verify_result->get_error_message() );
		}

		// Connect class expects function defined in this file.
		require_once ABSPATH . '/wp-admin/includes/plugin.php';

		$url = $this->get_upgrade_url( $license_key );

		return $this->response(
			[
				'url'      => $url,
				'settings' => $this->service->get_settings(),
			]
		);
	}

	/**
	 * Install plugins.
	 *
	 * @since 1.2.0
	 *
	 * @param WP_REST_Request $request Current request.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function install_plugins( $request ) {

		$data = $request->get_json_params();

		if ( empty( $data['plugins'] ) ) {
			return $this->error( __( 'Missing plugin list.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		$plugins = isset( $data['plugins'] ) && is_array( $data['plugins'] )
			? array_map( 'sanitize_key', $data['plugins'] )
			: [];

		if ( empty( $plugins ) ) {
			return $this->error( __( 'Missing plugin list.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		$installer = Plugin::get( PluginInstallerService::class );
		$result    = $installer->install( $plugins );

		$response_data = [
			'plugins' => [
				'smtp'   => $installer->detect_smtp_plugins(),
				'addons' => $result['installed'],
			],
		];

		if ( ! empty( $result['errors'] ) ) {
			$response_data['errors'] = $result['errors'];
		}

		return $this->response( $response_data );
	}

	/**
	 * Start migration.
	 *
	 * Validates the source, checks entity dependencies, initializes migration state,
	 * and returns entity counts for the selected entities.
	 *
	 * @since 1.3.1
	 *
	 * @param WP_REST_Request $request Current request.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function migration_start( $request ) {

		$data      = $request->get_json_params();
		$source_id = isset( $data['source'] ) ? sanitize_key( $data['source'] ) : '';
		$entities  = isset( $data['entities'] ) && is_array( $data['entities'] )
			? array_map( 'sanitize_key', $data['entities'] )
			: [];

		if ( empty( $source_id ) ) {
			return $this->error( esc_html__( 'Missing migration source.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		if ( empty( $entities ) ) {
			return $this->error( esc_html__( 'No entities selected for import.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		$result = Plugin::get( SetupWizardMigrationHandler::class )->start( $source_id, $entities );

		if ( is_wp_error( $result ) ) {
			return $this->error( $result->get_error_message() );
		}

		return $this->response( $result );
	}

	/**
	 * Process a migration batch.
	 *
	 * Processes one batch of items and returns progress.
	 * Called repeatedly by the wizard until `done: true`.
	 *
	 * @since 1.3.1
	 *
	 * @param WP_REST_Request $request Current request.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function migration_process_batch( $request ) {

		$data      = $request->get_json_params();
		$source_id = isset( $data['source'] ) ? sanitize_key( $data['source'] ) : '';

		if ( empty( $source_id ) ) {
			return $this->error( esc_html__( 'Missing migration source.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		$result = Plugin::get( SetupWizardMigrationHandler::class )->process_batch( $source_id );

		if ( is_wp_error( $result ) ) {
			return $this->error( $result->get_error_message() );
		}

		return $this->response( $result );
	}

	/**
	 * Complete migration.
	 *
	 * Finalizes the migration and saves results to the options table.
	 * Includes `context: "setup_wizard"` flag in the saved metadata.
	 *
	 * @since 1.3.1
	 *
	 * @param WP_REST_Request $request Current request.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function migration_complete( $request ) {

		$data      = $request->get_json_params();
		$source_id = isset( $data['source'] ) ? sanitize_key( $data['source'] ) : '';

		if ( empty( $source_id ) ) {
			return $this->error( esc_html__( 'Missing migration source.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		$result = Plugin::get( SetupWizardMigrationHandler::class )->complete( $source_id );

		if ( is_wp_error( $result ) ) {
			return $this->error( $result->get_error_message() );
		}

		return $this->response( $result );
	}

	/**
	 * Output response.
	 *
	 * @since 1.2.0
	 *
	 * @param mixed $data    Response data.
	 * @param bool  $success Response status.
	 *
	 * @return WP_REST_Response
	 */
	private function response( $data = [], $success = true ) {

		$data = [
			'success' => $success,
			'data'    => $data,
		];

		return rest_ensure_response( $data );
	}

	/**
	 * Output error.
	 *
	 * @since 1.2.0
	 *
	 * @param string $message     Error message.
	 * @param int    $status_code Error status code.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	private function error( $message, $status_code = 200 ) {

		if ( $status_code !== 200 ) {
			return new WP_Error(
				'auth',
				$message,
				[
					'status' => $status_code,
				]
			);
		}

		return $this->response(
			[
				'message' => $message,
			],
			false
		);
	}

	/**
	 * Get upgrade URL.
	 *
	 * @since 1.2.0
	 *
	 * @param string $license_key License key.
	 *
	 * @return string
	 */
	private function get_upgrade_url( $license_key ) {

		return Connect::generate_url( $license_key, '', $this->get_upgrade_redirect_url() );
	}

	/**
	 * Get the redirect URL for the plugin upgrade flow.
	 *
	 * @since 1.2.0
	 *
	 * @return string
	 */
	private function get_upgrade_redirect_url() {

		return Plugin::get( SetupWizard::class )->get_url( '/complete' );
	}
}
