<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// phpcs:disable WordPress.Security.NonceVerification.Recommended, WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
// Justification: OAuth callbacks cannot use a WordPress nonce — the user bounces through Dropbox's authorization server, so a session-bound nonce we set before the outbound redirect would not survive. Instead the OAuth `state` parameter (a per-request random token stored in a transient and verified on return) protects against CSRF. wp_redirect() is intentional for the outgoing redirect to Dropbox's authorization endpoint, which is not in WordPress's allowed_redirect_hosts and would be blocked by wp_safe_redirect().

class CCGAL_Dropbox_OAuth {

	const AUTH_URL  = 'https://www.dropbox.com/oauth2/authorize';
	const TOKEN_URL = 'https://api.dropboxapi.com/oauth2/token';

	public static function init(): void {
		add_action( 'admin_post_ccgal_dropbox_oauth_start',      array( __CLASS__, 'handle_start' ) );
		add_action( 'admin_post_ccgal_dropbox_oauth_callback',   array( __CLASS__, 'handle_callback' ) );
		add_action( 'admin_post_ccgal_dropbox_oauth_disconnect', array( __CLASS__, 'handle_disconnect' ) );
	}

	public static function get_redirect_uri(): string {
		return admin_url( 'admin-post.php?action=ccgal_dropbox_oauth_callback' );
	}

	public static function get_app_key(): string {
		return trim( (string) get_option( 'ccgal_dropbox_app_key', '' ) );
	}

	public static function get_app_secret(): string {
		$enc = (string) get_option( 'ccgal_dropbox_app_secret', '' );
		return $enc ? CCGAL_Encryption::decrypt( $enc ) : '';
	}

	public static function save_app_credentials( string $app_key, string $app_secret ): void {
		update_option( 'ccgal_dropbox_app_key', sanitize_text_field( $app_key ), false );
		if ( '' !== $app_secret ) {
			update_option( 'ccgal_dropbox_app_secret', CCGAL_Encryption::encrypt( $app_secret ), false );
		}
	}

	public static function get_tokens(): ?array {
		$enc = (string) get_option( 'ccgal_dropbox_tokens', '' );
		if ( ! $enc ) {
			return null;
		}
		$json = CCGAL_Encryption::decrypt( $enc );
		if ( ! $json ) {
			return null;
		}
		$tokens = json_decode( $json, true, 32 );
		return is_array( $tokens ) ? $tokens : null;
	}

	public static function save_tokens( array $tokens ): void {
		if ( ! isset( $tokens['obtained_at'] ) ) {
			$tokens['obtained_at'] = time();
		}
		update_option( 'ccgal_dropbox_tokens', CCGAL_Encryption::encrypt( wp_json_encode( $tokens ) ), false );
	}

	public static function is_connected(): bool {
		$t = self::get_tokens();
		return is_array( $t ) && ! empty( $t['refresh_token'] );
	}

	public static function handle_start(): void {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'Permission denied.', 'customer-cloud-gallery' ) );
		}
		check_admin_referer( 'ccgal_dropbox_oauth_start' );

		$app_key = self::get_app_key();
		if ( empty( $app_key ) ) {
			wp_safe_redirect( add_query_arg( 'ccgal_msg', 'missing_credentials', admin_url( 'admin.php?page=ccgal-settings' ) ) );
			exit;
		}

		$state = wp_generate_password( 32, false, false );
		set_transient( 'ccgal_dropbox_oauth_state_' . $state, get_current_user_id(), 600 );

		$params = array(
			'client_id'         => $app_key,
			'redirect_uri'      => self::get_redirect_uri(),
			'response_type'     => 'code',
			'token_access_type' => 'offline',
			'scope'             => 'files.metadata.read files.content.read',
			'state'             => $state,
		);

		// External redirect to Dropbox's OAuth endpoint — wp_safe_redirect would block off-host targets.
		wp_redirect( esc_url_raw( self::AUTH_URL . '?' . http_build_query( $params ) ) );
		exit;
	}

	public static function handle_callback(): void {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'Permission denied.', 'customer-cloud-gallery' ) );
		}

		$state = isset( $_GET['state'] ) ? sanitize_text_field( wp_unslash( $_GET['state'] ) ) : '';
		$code  = isset( $_GET['code'] )  ? sanitize_text_field( wp_unslash( $_GET['code'] ) )  : '';
		$error = isset( $_GET['error'] ) ? sanitize_text_field( wp_unslash( $_GET['error'] ) ) : '';

		if ( $error ) {
			wp_safe_redirect( add_query_arg( 'ccgal_msg', 'oauth_error', admin_url( 'admin.php?page=ccgal-settings' ) ) );
			exit;
		}

		$user_id = get_transient( 'ccgal_dropbox_oauth_state_' . $state );
		delete_transient( 'ccgal_dropbox_oauth_state_' . $state );

		if ( ! $user_id || (int) $user_id !== get_current_user_id() ) {
			wp_die( esc_html__( 'Invalid state. Please retry the connection.', 'customer-cloud-gallery' ) );
		}

		$response = wp_remote_post(
			self::TOKEN_URL,
			array(
				'timeout' => 30,
				'body'    => array(
					'code'          => $code,
					'client_id'     => self::get_app_key(),
					'client_secret' => self::get_app_secret(),
					'redirect_uri'  => self::get_redirect_uri(),
					'grant_type'    => 'authorization_code',
				),
			)
		);

		if ( is_wp_error( $response ) ) {
			wp_safe_redirect( add_query_arg( 'ccgal_msg', 'token_error', admin_url( 'admin.php?page=ccgal-settings' ) ) );
			exit;
		}

		$body = json_decode( wp_remote_retrieve_body( $response ), true, 32 );
		if ( empty( $body['access_token'] ) ) {
			wp_safe_redirect( add_query_arg( 'ccgal_msg', 'token_error', admin_url( 'admin.php?page=ccgal-settings' ) ) );
			exit;
		}

		self::save_tokens( $body );
		wp_safe_redirect( add_query_arg( 'ccgal_msg', 'connected', admin_url( 'admin.php?page=ccgal-settings' ) ) );
		exit;
	}

	public static function handle_disconnect(): void {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'Permission denied.', 'customer-cloud-gallery' ) );
		}
		check_admin_referer( 'ccgal_dropbox_oauth_disconnect' );
		delete_option( 'ccgal_dropbox_tokens' );
		wp_safe_redirect( add_query_arg( 'ccgal_msg', 'disconnected', admin_url( 'admin.php?page=ccgal-settings' ) ) );
		exit;
	}

	public static function get_access_token() {
		$tokens = self::get_tokens();
		if ( ! $tokens ) {
			return new WP_Error( 'ccgal_not_connected', __( 'Dropbox is not connected.', 'customer-cloud-gallery' ) );
		}

		$expires_in  = isset( $tokens['expires_in'] ) ? (int) $tokens['expires_in'] : 14400;
		$obtained_at = isset( $tokens['obtained_at'] ) ? (int) $tokens['obtained_at'] : 0;
		$is_expired  = ( $obtained_at + $expires_in - 60 ) < time();

		if ( ! $is_expired && ! empty( $tokens['access_token'] ) ) {
			return $tokens['access_token'];
		}

		if ( empty( $tokens['refresh_token'] ) ) {
			return new WP_Error( 'ccgal_no_refresh_token', __( 'No refresh token. Please reconnect Dropbox.', 'customer-cloud-gallery' ) );
		}

		$response = wp_remote_post(
			self::TOKEN_URL,
			array(
				'timeout' => 30,
				'body'    => array(
					'client_id'     => self::get_app_key(),
					'client_secret' => self::get_app_secret(),
					'refresh_token' => $tokens['refresh_token'],
					'grant_type'    => 'refresh_token',
				),
			)
		);

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

		$body = json_decode( wp_remote_retrieve_body( $response ), true, 32 );
		if ( empty( $body['access_token'] ) ) {
			return new WP_Error( 'ccgal_token_refresh_failed', __( 'Access token could not be refreshed.', 'customer-cloud-gallery' ) );
		}

		$tokens['access_token'] = $body['access_token'];
		$tokens['expires_in']   = isset( $body['expires_in'] ) ? (int) $body['expires_in'] : 14400;
		$tokens['obtained_at']  = time();
		self::save_tokens( $tokens );

		return $tokens['access_token'];
	}
}
