<?php
/**
 * Google Sheets Authentication Class
 *
 * @package ShopExplorer\GoogleSheets
 * @since 1.0.0
 */

namespace SHOPEXPLORER\GoogleSheets;

use SHOPEXPLORER\Exceptions\GoogleSheetsException;
use SHOPEXPLORER\Exceptions\ConfigurationException;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

/**
 * Google Sheets Authentication class
 *
 * Handles OAuth 2.0 authentication for Google Sheets API
 *
 * @since 1.0.0
 */
class SHOPEX_GoogleSheetsAuth {

	/**
	 * Google Client instance
	 *
	 * @var \Google_Client|null
	 */
	private $client;

	/**
	 * OAuth scopes required
	 *
	 * @var array
	 */
	private $scopes = [
		\Google_Service_Sheets::SPREADSHEETS,
		\Google_Service_Drive::DRIVE_FILE,
	];

	/**
	 * Credentials file path
	 *
	 * @var string
	 */
	private $credentials_path;

	/**
	 * Token storage option name
	 *
	 * @var string
	 */
	private $token_option = 'shopex_google_token';

	/**
	 * Credentials option name
	 *
	 * @var string
	 */
	private $credentials_option = 'shopex_google_credentials';

	/**
	 * Constructor
	 */
	public function __construct() {
		$upload_dir = wp_upload_dir();
		$this->credentials_path = $upload_dir['basedir'] . '/shop-explorer/';
		$this->init_client();
	}

	/**
	 * Initialize Google Client
	 */
	private function init_client() {
		if ( ! class_exists( 'Google_Client' ) ) {
			return;
		}

		try {
			$this->client = new \Google_Client();
			$this->client->setApplicationName( 'Shop Explorer' );
			$this->client->setScopes( $this->scopes );
			$this->client->setAccessType( 'offline' );
			$this->client->setPrompt( 'select_account consent' );

			// Set credentials if available
			$credentials = $this->get_stored_credentials();
			if ( $credentials ) {
				$this->client->setAuthConfig( $credentials );
			}

			// Set access token if available
			$token = $this->get_stored_token();
			if ( $token ) {
				$this->client->setAccessToken( $token );

				// Refresh token if expired
				if ( $this->client->isAccessTokenExpired() ) {
					$this->refresh_access_token();
				}
			}
		} catch ( \Exception $e ) {
			return false;
		}
	}

	/**
	 * Set OAuth credentials
	 *
	 * @param array $credentials OAuth credentials
	 * @return bool
	 * @throws GoogleSheetsException
	 */
	public function set_credentials( $credentials ) {
		if ( ! $this->client ) {
			return false;
		}

		try {
			// Validate credentials structure
			$this->validate_credentials( $credentials );

			// Store credentials securely
			$encrypted_credentials = $this->encrypt_credentials( $credentials );
			update_option( $this->credentials_option, $encrypted_credentials );

			// Set credentials in client
			$this->client->setAuthConfig( $credentials );

			return true;

		} catch ( \Exception $e ) {
			return false;
		}
	}

	/**
	 * Set OAuth credentials from JSON file
	 *
	 * @param string $json_file_path Path to JSON credentials file
	 * @return bool
	 * @throws GoogleSheetsException
	 */
	public function set_credentials_from_file( $json_file_path ) {
		if ( ! file_exists( $json_file_path ) ) {
			return false;
		}

		$json_content = file_get_contents( $json_file_path );
		$credentials = json_decode( $json_content, true );

		if ( json_last_error() !== JSON_ERROR_NONE ) {
			return false;
		}

		return $this->set_credentials( $credentials );
	}

	/**
	 * Get authorization URL
	 *
	 * @return string
	 * @throws GoogleSheetsException
	 */
	public function get_authorization_url() {
		if ( ! $this->client ) {
			return false;
		}

		if ( ! $this->has_credentials() ) {
			return false;
		}

		// Set redirect URI
		$redirect_uri = admin_url( 'admin.php?page=shop-explorer-settings&tab=google-sheets&action=oauth-callback' );
		$this->client->setRedirectUri( $redirect_uri );

		return $this->client->createAuthUrl();
	}

	/**
	 * Handle OAuth callback
	 *
	 * @param string $authorization_code Authorization code from OAuth callback
	 * @return bool
	 * @throws GoogleSheetsException
	 */
	public function handle_oauth_callback( $authorization_code ) {
		if ( ! $this->client ) {
			return false;
		}

		try {
			// Exchange authorization code for access token
			$token = $this->client->fetchAccessTokenWithAuthCode( $authorization_code );

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

			// Store the token
			$this->store_token( $token );

			return true;

		} catch ( \Exception $e ) {
			return false;
		}
	}

	/**
	 * Refresh access token
	 *
	 * @return bool
	 * @throws GoogleSheetsException
	 */
	public function refresh_access_token() {
		if ( ! $this->client ) {
			return false;
		}

		try {
			$refresh_token = $this->client->getRefreshToken();

			if ( ! $refresh_token ) {
				return false;
			}

			$new_token = $this->client->fetchAccessTokenWithRefreshToken( $refresh_token );

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

			// Store the new token
			$this->store_token( $new_token );

			return true;

		} catch ( \Exception $e ) {
			return false;
		}
	}

	/**
	 * Get authenticated Google Client
	 *
	 * @return \Google_Client|null
	 */
	public function get_authenticated_client() {
		if ( ! $this->is_authenticated() ) {
			return null;
		}

		return $this->client;
	}

	/**
	 * Check if authenticated
	 *
	 * @return bool
	 */
	public function is_authenticated() {
		if ( ! $this->client ) {
			return false;
		}

		$token = $this->client->getAccessToken();

		if ( ! $token ) {
			return false;
		}

		// Check if token is expired and can be refreshed
		if ( $this->client->isAccessTokenExpired() ) {
			try {
				$this->refresh_access_token();
				return true;
			} catch ( \Exception $e ) {
				return false;
			}
		}

		return true;
	}

	/**
	 * Check if credentials are set
	 *
	 * @return bool
	 */
	public function has_credentials() {
		return ! empty( $this->get_stored_credentials() );
	}

	/**
	 * Revoke authentication
	 *
	 * @return bool
	 */
	public function revoke_authentication() {
		try {
			if ( $this->client && $this->client->getAccessToken() ) {
				$this->client->revokeToken();
			}

			// Clear stored tokens and credentials
			delete_option( $this->token_option );
			delete_option( $this->credentials_option );

			return true;

		} catch ( \Exception $e ) {
			return false;
		}
	}

	/**
	 * Get authentication status
	 *
	 * @return array
	 */
	public function get_auth_status() {
		$status = [
			'has_credentials' => $this->has_credentials(),
			'is_authenticated' => false,
			'token_expires_at' => null,
			'scopes' => $this->scopes,
		];

		if ( $this->client && $this->client->getAccessToken() ) {
			$status['is_authenticated'] = ! $this->client->isAccessTokenExpired();

			$token = $this->client->getAccessToken();
			if ( isset( $token['expires_in'] ) ) {
				$status['token_expires_at'] = time() + $token['expires_in'];
			}
		}

		return $status;
	}

	/**
	 * Store access token
	 *
	 * @param array $token Access token
	 */
	private function store_token( $token ) {
		$encrypted_token = $this->encrypt_token( $token );
		update_option( $this->token_option, $encrypted_token );
	}

	/**
	 * Get stored access token
	 *
	 * @return array|null
	 */
	private function get_stored_token() {
		$encrypted_token = get_option( $this->token_option );

		if ( ! $encrypted_token ) {
			return null;
		}

		return $this->decrypt_token( $encrypted_token );
	}

	/**
	 * Get stored credentials
	 *
	 * @return array|null
	 */
	private function get_stored_credentials() {
		$encrypted_credentials = get_option( $this->credentials_option );

		if ( ! $encrypted_credentials ) {
			return null;
		}

		return $this->decrypt_credentials( $encrypted_credentials );
	}

	/**
	 * Validate credentials structure
	 *
	 * @param array $credentials Credentials to validate
	 * @throws GoogleSheetsException
	 */
	private function validate_credentials( $credentials ) {
		$required_fields = [ 'client_id', 'client_secret', 'redirect_uris' ];

		// Check for web credentials structure
		if ( isset( $credentials['web'] ) ) {
			$credentials = $credentials['web'];
		} elseif ( isset( $credentials['installed'] ) ) {
			$credentials = $credentials['installed'];
		}

		foreach ( $required_fields as $field ) {
			if ( ! isset( $credentials[ $field ] ) ) {
				return false;
			}
		}
	}

	/**
	 * Encrypt credentials for storage
	 *
	 * @param array $credentials Credentials to encrypt
	 * @return string
	 */
	private function encrypt_credentials( $credentials ) {
		$key = $this->get_encryption_key();
		$serialized = serialize( $credentials );
		$encrypted = openssl_encrypt( $serialized, 'AES-256-CBC', $key, 0, substr( $key, 0, 16 ) );
		return base64_encode( $encrypted );
	}

	/**
	 * Decrypt credentials from storage
	 *
	 * @param string $encrypted_credentials Encrypted credentials
	 * @return array|null
	 */
	private function decrypt_credentials( $encrypted_credentials ) {
		try {
			$key = $this->get_encryption_key();
			$encrypted = base64_decode( $encrypted_credentials );
			$decrypted = openssl_decrypt( $encrypted, 'AES-256-CBC', $key, 0, substr( $key, 0, 16 ) );
			return unserialize( $decrypted );
		} catch ( \Exception $e ) {
			return null;
		}
	}

	/**
	 * Encrypt token for storage
	 *
	 * @param array $token Token to encrypt
	 * @return string
	 */
	private function encrypt_token( $token ) {
		$key = $this->get_encryption_key();
		$serialized = serialize( $token );
		$encrypted = openssl_encrypt( $serialized, 'AES-256-CBC', $key, 0, substr( $key, 0, 16 ) );
		return base64_encode( $encrypted );
	}

	/**
	 * Decrypt token from storage
	 *
	 * @param string $encrypted_token Encrypted token
	 * @return array|null
	 */
	private function decrypt_token( $encrypted_token ) {
		try {
			$key = $this->get_encryption_key();
			$encrypted = base64_decode( $encrypted_token );
			$decrypted = openssl_decrypt( $encrypted, 'AES-256-CBC', $key, 0, substr( $key, 0, 16 ) );
			return unserialize( $decrypted );
		} catch ( \Exception $e ) {
			return null;
		}
	}

	/**
	 * Get encryption key
	 *
	 * @return string
	 */
	private function get_encryption_key() {
		$key = get_option( 'shopex_encryption_key' );

		if ( ! $key ) {
			$key = wp_generate_password( 32, false );
			update_option( 'shopex_encryption_key', $key );
		}

		return hash( 'sha256', $key . SECURE_AUTH_KEY );
	}

	/**
	 * Create credentials directory if it doesn't exist
	 */
	private function ensure_credentials_directory() {
		if ( ! file_exists( $this->credentials_path ) ) {
			wp_mkdir_p( $this->credentials_path );

			// Add .htaccess to protect directory
			$htaccess_content = "Order deny,allow\nDeny from all\n";
			file_put_contents( $this->credentials_path . '.htaccess', $htaccess_content );
		}
	}

	/**
	 * Get redirect URI for OAuth
	 *
	 * @return string
	 */
	public function get_redirect_uri() {
		return admin_url( 'admin.php?page=shop-explorer-settings&tab=google-sheets&action=oauth-callback' );
	}

	/**
	 * Test authentication with a simple API call
	 *
	 * @return bool
	 */
	public function test_authentication() {
		if ( ! $this->is_authenticated() ) {
			return false;
		}

		try {
			$service = new \Google_Service_Sheets( $this->client );

			// Try to create a test spreadsheet and delete it
			$spreadsheet = new \Google_Service_Sheets_Spreadsheet();
			$spreadsheet->setProperties(
				( new \Google_Service_Sheets_SpreadsheetProperties() )->setTitle( 'Shop Explorer Test - ' . time() )
			);

			$response = $service->spreadsheets->create( $spreadsheet );
			$spreadsheet_id = $response->getSpreadsheetId();

			// Clean up test spreadsheet
			$drive_service = new \Google_Service_Drive( $this->client );
			$drive_service->files->delete( $spreadsheet_id );

			return true;

		} catch ( \Exception $e ) {
			return false;
		}
	}
}
