<?php

namespace SugarCalendarBookings\Admin\Settings;

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

use SugarCalendarBookings\Integrations\ExternalBusyTimes\ExternalBusyTimeModel;
use SugarCalendarBookings\Integrations\OAuthRelay\OAuthConnectionModel;
use SugarCalendarBookings\Integrations\OAuthRelay\OAuthRelayClient;
use SugarCalendarBookings\Plugin;
use SugarCalendarBookings\Services\UnbookableServicesDisabler;
use SugarCalendarBookings\Support\Ajax\AbstractController;
use SugarCalendarBookings\Support\Bootable;
use SugarCalendarBookings\Vendor\ProductApi\Auth\AuthOptions;
use SugarCalendarBookings\Vendor\ProductApi\Auth\SiteRegistration;
use SugarCalendarBookings\Vendor\ProductApi\Options as ProductApiOptions;
use SugarCalendarBookings\Vendor\ProductApi\ProductApi;

/**
 * Integrations controller.
 *
 * @since 1.4.0
 */
class IntegrationsController extends AbstractController implements Bootable {

	/**
	 * Register hooks.
	 *
	 * @since 1.4.0
	 */
	public function hooks() {

		add_action( 'admin_init', [ $this, 'connect_integration' ] );
	}

	/**
	 * Handle integration connect flow.
	 *
	 * Auto-registers the site with the Product API if needed,
	 * then redirects to the OAuth authorization URL.
	 *
	 * @since 1.4.0
	 */
	public function connect_integration(): void { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( ! isset( $_GET['action'] ) || $_GET['action'] !== 'scb_integration_connect' ) {
			return;
		}

		// Verify user has permission (admin or employee).
		if ( ! current_user_can( 'manage_options' ) && ! current_user_can( 'scb_employee_access' ) ) { // phpcs:ignore WordPress.WP.Capabilities.Unknown
			return;
		}

		// Verify nonce.
		// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
		if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( wp_unslash( $_GET['_wpnonce'] ), 'scb_integration_connect' ) ) {
			return;
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$provider = isset( $_GET['provider'] ) ? sanitize_key( wp_unslash( $_GET['provider'] ) ) : '';

		if ( empty( $provider ) ) {
			return;
		}

		$registration = ProductApi::get( SiteRegistration::class );

		// Reset stale registration when domain has changed.
		$this->maybe_reset_stale_registration( $registration );

		if ( ! $registration->is_registered() ) {

			// Employees cannot trigger site registration — admin must do it.
			if ( ! current_user_can( 'manage_options' ) ) {
				$this->redirect_registration_failed(
					$provider,
					esc_html__( 'Site registration is required. Please ask an administrator to connect the integration first.', 'sugar-calendar-bookings-scheduling-appointments-lite' )
				);

				return;
			}

			// Clear rate limit — admin-initiated connects should not be blocked.
			ProductApi::get( ProductApiOptions::class )->delete_transient( 'rate_limit_auth_register_site' );

			$result = $registration->register();

			if ( is_wp_error( $result ) ) {
				$this->redirect_registration_failed( $provider, sanitize_text_field( $result->get_error_message() ) );

				return;
			}
		}

		// Build the OAuth authorization URL.
		$return_url = add_query_arg(
			[
				'action'      => 'scbookings_oauth_callback',
				'redirect_to' => rawurlencode( IntegrationsSettingsTab::get_url() ),
			],
			admin_url( 'admin.php' )
		);

		$relay_client = new OAuthRelayClient();
		$auth_url     = $relay_client->get_authorization_url( $provider, $return_url, 'integrations' );

		// phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
		wp_redirect( $auth_url );
		exit;
	}

	/**
	 * Reset stale site registration when domain has changed.
	 *
	 * When the site URL changes, the relay still associates the old domain with
	 * the existing installation ID. Clearing credentials and generating a new
	 * installation ID forces a clean re-registration with the new domain.
	 *
	 * @since 1.4.0
	 *
	 * @param SiteRegistration $registration SiteRegistration instance.
	 */
	private function maybe_reset_stale_registration( SiteRegistration $registration ): void {

		// Already registered with the current URL — nothing to do.
		if ( $registration->is_registered() ) {
			return;
		}

		$auth_options = ProductApi::get( AuthOptions::class );

		// No credentials stored — first-time registration, no reset needed.
		if ( empty( $auth_options->get( 'site_id' ) ) ) {
			return;
		}

		// Credentials exist but current URL isn't registered → domain changed.
		// Clear auth credentials so register() starts fresh.
		$auth_options->update(
			[
				'site_id'          => '',
				'signing_secret'   => '',
				'verification_key' => '',
				'site_urls'        => [],
			]
		);

		// Generate a new installation ID so the relay creates a fresh site entry
		// instead of returning stale credentials for the old domain.
		$options = ProductApi::get( ProductApiOptions::class );

		$options->update(
			[
				'wp_installation_id' => substr( wp_hash( wp_generate_uuid4() ), 0, 30 ),
			]
		);

		// Clear rate limit so the fresh registration isn't blocked by previous attempts.
		$options->delete_transient( 'rate_limit_auth_register_site' );
	}

	/**
	 * Redirect back to integrations page with a registration failure notice.
	 *
	 * @since 1.4.0
	 *
	 * @param string $provider      Provider slug.
	 * @param string $error_message Error message to display.
	 */
	private function redirect_registration_failed( string $provider, string $error_message ): void {

		wp_safe_redirect(
			add_query_arg(
				[
					'scbookings-notice'       => 'registration_failed',
					'scbookings-notice-data'  => rawurlencode( $provider ),
					'scbookings-notice-error' => rawurlencode( $error_message ),
				],
				IntegrationsSettingsTab::get_url()
			)
		);
		exit;
	}

	/**
	 * Force a token refresh for a connection.
	 *
	 * Triggered by the "Refresh Token" button when a connection has an auth error.
	 *
	 * @since 1.4.0
	 */
	public function refresh_token(): void { // phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( esc_html__( 'You are not allowed to perform this action.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		$data = $this->get_input_data(
			[
				'id' => 'required|id',
			]
		);

		if ( is_wp_error( $data ) ) {
			wp_send_json_error( $data->get_error_message() );
		}

		$connection = OAuthConnectionModel::get( $data['id'] );

		if ( ! $connection ) {
			wp_send_json_error( esc_html__( 'Connection not found.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		$relay  = new OAuthRelayClient();
		$result = $relay->refresh_token( $connection->refresh_token, $connection->app_id );

		if ( is_wp_error( $result ) ) {
			wp_send_json_error(
				[
					'code'    => $result->get_error_code(),
					'message' => $result->get_error_message(),
				]
			);
		}

		// Update tokens on success.
		if ( ! empty( $result['tokens']['access_token'] ) && ! empty( $result['tokens']['expires_in'] ) ) {
			$connection->access_token = $result['tokens']['access_token'];
			$connection->expires_at   = gmdate( 'Y-m-d H:i:s', time() + $result['tokens']['expires_in'] );
			$connection->refreshed_at = current_time( 'mysql', true );

			if ( ! empty( $result['tokens']['refresh_token'] ) ) {
				$connection->refresh_token = $result['tokens']['refresh_token'];
			}

			if ( $connection->status === 'auth_error' ) {
				$connection->status = 'active';
			}

			$connection->save();
		}

		wp_send_json_success(
			[
				'message' => esc_html__( 'Token refresh successful.', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
			]
		);
	}

	/**
	 * Delete an OAuth connection.
	 *
	 * @since 1.4.0
	 */
	public function delete_connection(): void {

		if ( ! current_user_can( 'manage_options' ) && ! current_user_can( 'scb_employee_access' ) ) { // phpcs:ignore WordPress.WP.Capabilities.Unknown
			wp_send_json_error( esc_html__( 'You are not allowed to perform this action.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		$data = $this->get_input_data(
            [
				'id' => 'required|id',
            ]
        );

		if ( is_wp_error( $data ) ) {
			wp_send_json_error( $data->get_error_message() );
		}

		$connection = OAuthConnectionModel::get( $data['id'] );

		if ( ! $connection ) {
			wp_send_json_error( esc_html__( 'Connection not found.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		$is_owner = (int) $connection->user_id === get_current_user_id();
		$is_admin = current_user_can( 'manage_options' );

		if ( ! $is_owner && ! $is_admin ) {
			wp_send_json_error( esc_html__( 'Connection not found.', 'sugar-calendar-bookings-scheduling-appointments-lite' ) );
		}

		// Unregister webhook from the Product API relay.
		( new OAuthRelayClient() )->unregister_webhook( $connection->provider, $connection->account_id );

		// Delete associated external busy times.
		ExternalBusyTimeModel::query()
			->where( 'oauth_connection_id', '=', $connection->id, '%d' )
			->delete();

		$provider_slug = $connection->provider;

		$connection->delete();

		// Disable services whose only bookable path was this provider and which
		// have nothing else left to fall back on.
		Plugin::get( UnbookableServicesDisabler::class )->disable_orphaned_services( $provider_slug );

		wp_send_json_success(
			[
				'message' => esc_html__( 'Connection removed successfully.', 'sugar-calendar-bookings-scheduling-appointments-lite' ),
			]
		);
	}
}
