<?php
/**
 * Calendar Auth Controller
 *
 * @package BookticsGoogleCalendar\Google\Controller
 */

namespace Booktics\Module\Google_Calendar\Controller;

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

use Booktics\Abstracts\Base_Rest_Controller;
use Exception;
use WP_REST_Request;
use WP_REST_Server;
use WP_Error;
use WP_HTTP_Response;

/**
 * Class Calendar_Auth_Controller
 */
class Calendar_Auth_Controller extends Base_Rest_Controller {

	/**
	 * The namespace of this controller's route.
	 *
	 * @var string
	 */
	protected $namespace = 'booktics/v1';

	/**
	 * The base of this controller's route.
	 *
	 * @var string
	 */
	protected $rest_base = 'settings/google';

	/**
	 * User meta key for storing Google tokens
	 */
	const BGC_USER_META_TOKEN = 'booktics_google_auth_token';

	/**
	 * Google OAuth configuration.
	 *
	 * @var array
	 */
	private $oauth_config;

	/**
	 * Initialize the class and set up hooks.
	 */
	public function __construct() {
		$this->oauth_config = $this->get_oauth_config();
		add_action( 'template_redirect', array( $this, 'handle_google_auth_callback' ) );

		// In your addon's main file or a dedicated class
		add_filter(
			'booktics_team_member_data',
			function ( $team_member, $team_member_id ) {
				$team_member['google_calendar_connected'] = $this->team_member_is_connected( $team_member_id );

				return $team_member;
			},
			10,
			2
		);
	}

	/**
	 * Get OAuth configuration for Google API
	 *
	 * @return array
	 */
	private function get_oauth_config() {
		return array(
			'client_id'     => booktics_get_option( 'google_client_id' ),
			'client_secret' => booktics_get_option( 'google_secret_key' ),
			'redirect_uri'  => booktics_get_option( 'google_redirect_url' ),
			'scope'         => 'https://www.googleapis.com/auth/calendar',
			'access_type'   => 'offline',
			'prompt'        => 'consent',
		);
	}

	/**
	 * Get valid access token for user, refreshing if necessary
	 *
	 * @param int $user_id
	 * @return string|false Access token or false on failure
	 */
	private function get_valid_access_token( $user_id = null ) {
		if ( null === $user_id ) {
			$user_id = get_current_user_id();
		}

		$token = get_user_meta( $user_id, self::BGC_USER_META_TOKEN, true );
		if ( ! $token ) {
			return false;
		}

		// Handle both string (old format) and array (new format) tokens
		if ( is_string( $token ) ) {
			$token = json_decode( $token, true );
		}

		if ( ! is_array( $token ) || empty( $token['access_token'] ) ) {
			return false;
		}

		// Check if token needs refreshing
		if ( $this->is_token_expired( $token ) ) {
			$refreshed_token = $this->refresh_access_token( $token, $user_id );
			if ( ! $refreshed_token ) {
				delete_user_meta( $user_id, self::BGC_USER_META_TOKEN );
				return false;
			}
			return $refreshed_token['access_token'];
		}

		return $token['access_token'];
	}

	/**
	 * Check if token is expired
	 *
	 * @param array $token
	 * @return bool
	 */
	private function is_token_expired( $token ) {
		if ( empty( $token['expires_at'] ) ) {
			return true;
		}

		$expires_at = is_numeric( $token['expires_at'] ) ? $token['expires_at'] : strtotime( $token['expires_at'] );
		$buffer_time = 300; // 5 minutes buffer

		return ( time() + $buffer_time ) >= $expires_at;
	}

	/**
	 * Refresh access token using refresh token
	 *
	 * @param array $token
	 * @param int $user_id
	 * @return array|false New token array or false on failure
	 */
	private function refresh_access_token( $token, $user_id ) {
		if ( empty( $token['refresh_token'] ) ) {
			return false;
		}

		$response = wp_remote_post(
			'https://oauth2.googleapis.com/token',
			array(
				'body' => array(
					'client_id'     => $this->oauth_config['client_id'],
					'client_secret' => $this->oauth_config['client_secret'],
					'refresh_token' => $token['refresh_token'],
					'grant_type'    => 'refresh_token',
				),
			)
		);

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

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

		if ( isset( $new_token['error'] ) ) {
			return false;
		}

		// Merge with existing token to preserve refresh_token
		$refreshed_token = array_merge( $token, $new_token );

		// Set expiry time
		if ( isset( $refreshed_token['expires_in'] ) ) {
			$refreshed_token['expires_at'] = time() + $refreshed_token['expires_in'];
		}

		// Save the refreshed token
		update_user_meta( $user_id, self::BGC_USER_META_TOKEN, $refreshed_token );

		return $refreshed_token;
	}

	/**
	 * Handle direct OAuth callback from Google.
	 */
	public function handle_google_auth_callback() {
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Google OAuth callback doesnt require nonce
		if ( ! isset( $_GET['code'] ) ) {
			return;
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Google OAuth callback doesnt require nonce
		if ( isset( $_GET['state'] ) ) {
			// phpcs:ignore WordPress.Security.NonceVerification.Recommended 
			$decoded_state = base64_decode( sanitize_text_field( wp_unslash( $_GET['state'] ) ) );
			$state_data     = json_decode( $decoded_state, true );
			$team_member_id = isset( $state_data['team_member_id'] ) ? $state_data['team_member_id'] : null;
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended 
		$code = sanitize_text_field( wp_unslash( $_GET['code'] ) );

		if ( ! isset( $team_member_id ) ) {
			$team_member_id = get_current_user_id();
		}

		try {
			$tokens = $this->exchange_code_for_tokens( $code );

			if ( ! $tokens || isset( $tokens['error'] ) ) {
				throw new Exception( $tokens['error_description'] ?? 'Authentication failed' );
			}

			// Set expiry time
			if ( isset( $tokens['expires_in'] ) ) {
				$tokens['expires_at'] = time() + $tokens['expires_in'];
			}

			update_user_meta( $team_member_id, self::BGC_USER_META_TOKEN, $tokens );

			if ( current_user_can( 'manage_options' ) ) {
				booktics_update_option( 'google_calendar_connected', 1 );
			}

			$redirect_url = isset( $team_member_id ) && (int)$team_member_id != get_current_user_id() ? $this->get_redirect_url_for_team_member( $team_member_id ) : $this->get_redirect_url_for_admin();

			wp_safe_redirect( $redirect_url );
			exit;

		} catch ( Exception $e ) {
			$redirect_url = isset( $team_member_id ) ? $this->get_redirect_url_for_team_member( $team_member_id ) : $this->get_redirect_url_for_admin();

			wp_safe_redirect( $redirect_url );
			exit;
		}
	}

	/**
	 * Exchange authorization code for access tokens
	 *
	 * @param string $code
	 * @return array|false
	 */
	private function exchange_code_for_tokens( $code ) {
		$response = wp_remote_post(
			'https://oauth2.googleapis.com/token',
			array(
				'body' => array(
					'client_id'     => $this->oauth_config['client_id'],
					'client_secret' => $this->oauth_config['client_secret'],
					'code'          => $code,
					'grant_type'    => 'authorization_code',
					'redirect_uri'  => $this->oauth_config['redirect_uri'],
				),
			)
		);

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

		$body = wp_remote_retrieve_body( $response );
		return json_decode( $body, true );
	}

	private function get_redirect_url_for_admin() {
		return home_url( '/wp-admin/admin.php?page=booktics#/settings?tab=integration' );
	}

	private function get_redirect_url_for_team_member( $team_member_id ) {
		return home_url( "/wp-admin/admin.php?page=booktics#/team-members/update/{$team_member_id}?tab=integration" );
	}

	/**
	 * Register the routes for the controller.
	 */
	public function register_routes(): void {
		// Get Google auth URL
		register_rest_route(
			$this->namespace,
			$this->rest_base . '/connect',
			array(
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_auth_url' ),
					'permission_callback' => array( $this, 'permission_check' ),
					'args'                => array(
						'team_member_id' => array(
							'description' => __( 'ID of the team member to connect', 'booktics' ),
							'type'        => 'integer',
							'required'    => false,
						),
					),
				),
			)
		);

		// Revoke authentication
		register_rest_route(
			$this->namespace,
			$this->rest_base . '/revoke',
			array(
				array(
					'methods'             => WP_REST_Server::DELETABLE,
					'callback'            => array( $this, 'revoke_auth' ),
					'permission_callback' => array( $this, 'permission_check' ),
					'args'                => array(
						'team_member_id' => array(
							'description' => __( 'ID of the team member to disconnect', 'booktics' ),
							'type'        => 'integer',
							'required'    => false,
						),
					),
				),
			)
		);

		// Google connect for admin
		register_rest_route(
			$this->namespace,
			$this->rest_base . '/admin/connect',
			array(
				array(
					'methods'             => WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'get_admin_auth_url' ),
					'permission_callback' => function () {
						return current_user_can( 'manage_options' );
					},
				),
			)
		);

		// Check authentication status
		register_rest_route(
			$this->namespace,
			$this->rest_base . '/check',
			array(
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => array( $this, 'check_auth' ),
					'permission_callback' => array( $this, 'permission_check' ),
					'args'                => array(
						'team_member_id' => array(
							'description' => __( 'ID of the team member to check', 'booktics' ),
							'type'        => 'integer',
							'required'    => false,
						),
					),
				),
			)
		);
	}

	/**
	 * Check if a given request has permission.
	 *
	 * @return bool|WP_Error
	 */
	public function permission_check( $request ) {
		if ( ! is_user_logged_in() ) {
			return $this->error(
				__( 'You must be logged in to access this endpoint.', 'booktics' ),
				401,
				'permission_error'
			);
		}

		// Get team member ID from request
		$team_member_id = $this->get_team_member_id( $request );

		// If no team member ID specified, allow if user can manage options
		if ( ! $team_member_id ) {
			return current_user_can( 'manage_options' );
		}

		// If team member ID is specified, check if current user can edit that user
		if ( get_current_user_id() !== $team_member_id && ! current_user_can( 'edit_user', $team_member_id ) ) {
			return $this->error(
				__( 'You do not have permission to access this user\'s Google Calendar settings.', 'booktics' ),
				403,
				'permission_error'
			);
		}

		return true;
	}

	public function get_admin_auth_url( $request ) {
		$data                 = json_decode( $request->get_body(), true );
		$google_client_id     = $data['google_client_id'];
		$google_client_secret = $data['google_secret_key'];
		$block_booking_during_google_events = isset( $data['block_booking_during_google_events'] ) ? $data['block_booking_during_google_events'] : false;

		// save credentials in options
		booktics_update_option( 'google_client_id', $google_client_id );
		booktics_update_option( 'google_secret_key', $google_client_secret );
		booktics_update_option( 'block_booking_during_google_events', $block_booking_during_google_events );

		// Update OAuth config
		$this->oauth_config['client_id'] = $google_client_id;
		$this->oauth_config['client_secret'] = $google_client_secret;

		$auth_url = $this->create_auth_url();

		return $this->response(
			array( 'auth_url' => $auth_url ),
			__( 'Google authentication URL generated successfully.', 'booktics' )
		);
	}

	/**
	 * Get Google OAuth URL.
	 *
	 * @param WP_REST_Request $request
	 * @return WP_Error|WP_HTTP_Response
	 */
	public function get_auth_url( $request ) {
		try {
			// Get any parameters you want to pass through
			$state_params = array(
				'team_member_id' => absint( $request->get_param( 'team_member_id' ) ),
			);

			// Convert to JSON and base64 encode to make it URL-safe
			$state = base64_encode( wp_json_encode( $state_params ) );

			$auth_url = $this->create_auth_url( $state );

			if ( empty( $auth_url ) ) {
				return $this->error(
					__( 'Failed to generate Google authentication URL.', 'booktics' ),
					500,
					'auth_url_error'
				);
			}

			return $this->response(
				array( 'auth_url' => $auth_url ),
				__( 'Google authentication URL generated successfully.', 'booktics' )
			);

		} catch ( Exception $e ) {
			return $this->error(
				/* translators: %s: Error message */
				sprintf( __( 'Failed to generate Google auth URL: %s', 'booktics' ), $e->getMessage() ),
				500,
				'auth_error'
			);
		}
	}

	/**
	 * Create OAuth authorization URL
	 *
	 * @param string $state Optional state parameter
	 * @return string
	 */
	private function create_auth_url( $state = '' ) {
		$params = array(
			'client_id'     => $this->oauth_config['client_id'],
			'redirect_uri'  => $this->oauth_config['redirect_uri'],
			'scope'         => $this->oauth_config['scope'],
			'response_type' => 'code',
			'access_type'   => $this->oauth_config['access_type'],
			'prompt'        => $this->oauth_config['prompt'],
		);

		if ( ! empty( $state ) ) {
			$params['state'] = $state;
		}

		return 'https://accounts.google.com/o/oauth2/auth?' . http_build_query( $params );
	}

	/**
	 * Revoke Google authentication.
	 *
	 * @param WP_REST_Request $request
	 * @return WP_Error|WP_HTTP_Response
	 */
	public function revoke_auth( $request ) {
		try {
			$team_member_id = $this->get_team_member_id( $request );

			if ( current_user_can( 'manage_options' ) && (int)$team_member_id == get_current_user_id() ) {
				booktics_update_option( 'google_calendar_connected', 0 );
			}

			// Get the token before revoking
			$token = get_user_meta( $team_member_id, self::BGC_USER_META_TOKEN, true );

			if ( empty( $token ) ) {
				return $this->response(
					array( 'revoked' => true ),
					__( 'Google account was not connected.', 'booktics' )
				);
			}

			// Handle both string (old format) and array (new format) tokens
			if ( is_string( $token ) ) {
				$token = json_decode( $token, true );
			}

			// Revoke the token
			if ( isset( $token['access_token'] ) ) {
				$this->revoke_token( $token['access_token'] );
			}

			// Delete the token
			delete_user_meta( $team_member_id, self::BGC_USER_META_TOKEN );

			return $this->response(
				array( 'revoked' => true ),
				__( 'Google authentication revoked successfully.', 'booktics' )
			);

		} catch ( Exception $e ) {
			return $this->error(
				/* translators: %s: Error message */
				sprintf( __( 'Failed to revoke Google authentication: %s', 'booktics' ), $e->getMessage() ),
				500,
				'revoke_error'
			);
		}
	}

	/**
	 * Revoke access token at Google
	 *
	 * @param string $access_token
	 * @return bool
	 */
	private function revoke_token( $access_token ) {
		$response = wp_remote_post(
			'https://oauth2.googleapis.com/revoke',
			array(
				'body' => array(
					'token' => $access_token,
				),
			)
		);

		return ! is_wp_error( $response ) && wp_remote_retrieve_response_code( $response ) === 200;
	}

	/**
	 * Check authentication status.
	 *
	 * @param WP_REST_Request $request
	 * @return WP_Error|WP_HTTP_Response
	 */
	public function check_auth( $request ) {
		try {
			$team_member_id   = $this->get_team_member_id( $request );
			$token            = get_user_meta( $team_member_id, self::BGC_USER_META_TOKEN, true );
			$is_authenticated = false;

			if ( $token ) {
				// Handle both string (old format) and array (new format) tokens
				if ( is_string( $token ) ) {
					$token = json_decode( $token, true );
				}

				if ( is_array( $token ) && isset( $token['access_token'] ) ) {
					$is_authenticated = ! $this->is_token_expired( $token );

					// If token is expired but we have a refresh token, try to refresh
					if ( ! $is_authenticated && isset( $token['refresh_token'] ) ) {
						$new_token = $this->refresh_access_token( $token, $team_member_id );
						if ( $new_token ) {
							$is_authenticated = true;
						}
					}
				}
			}

			return $this->response(
				array( 'authenticated' => $is_authenticated ),
				$is_authenticated
					? __( 'Google account is connected.', 'booktics' )
					: __( 'Google account is not connected.', 'booktics' )
			);

		} catch ( Exception $e ) {
			return $this->error(
				/* translators: %s: Error message */
				sprintf( __( 'Failed to check authentication status: %s', 'booktics' ), $e->getMessage() ),
				500,
				'auth_check_error'
			);
		}
	}

	private function get_team_member_id( $request ) {
		$data = $request->get_method() === 'GET'
			? $request->get_query_params()
			: json_decode( $request->get_body(), true );

		if ( isset( $data['team_member_id'] ) ) {
			return absint( $data['team_member_id'] );
		}

		return get_current_user_id();
	}

	/**
	 * Check if team member is connected
	 *
	 * @param int $team_member_id team member id or user id
	 * @return int 1 if connected, 0 if not
	 */
	private function team_member_is_connected( $team_member_id ) {
		$token = get_user_meta( $team_member_id, self::BGC_USER_META_TOKEN, true );

		return empty( $token ) ? 0 : 1;
	}
}
