<?php
/**
 * A class handling the management interface.
 *
 * @since 3.0.0
 */

declare(strict_types=1);
namespace FeData\FastEvents\Core;

defined( 'ABSPATH' ) || exit;

use FeData\FastEvents\Logging\LoggerFactory;
use FeData\FastEvents\Storage\Options;
use WP_Error;
use WP_REST_Response;

/**
 * A class handling the management interface.
 */
class AdminUI {
	public const FE_ADMIN_UI_SETTINGS_INSTALL = 1;
	public const FE_ADMIN_UI_UPGRADE_PROCESS  = 2;
	public const FE_ADMIN_UI_DAILY_CHECK      = 3; // Triggered by the action-scheduler.

	/**
	 * Admin UI constructor.
	 *
	 * @since 2.0.3
	 *
	 * @param LoggerFactory $logger LoggerFactory class.
	 * @param Options       $options Options class.
	 */
	public function __construct( private readonly LoggerFactory $logger, private readonly Options $options ) {
		require_once ABSPATH . 'wp-admin/includes/image.php';
		require_once ABSPATH . 'wp-admin/includes/file.php';
		require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php';
		require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php';
		require_once ABSPATH . 'wp-includes/class-wp-error.php';
		WP_Filesystem();
	}

	/**
	 * Singletons should not be cloneable.
	 *
	 * @since 2.5.0
	 */
	private function __clone(): void {
	}

	/**
	 * Prevent from being unserialized.
	 *
	 * @since 2.5.0
	 */
	public function __wakeup(): void {
	}

	/**
	 * Get the latest version of the admin web interface from GitHub.
	 *
	 * @since 2.0
	 * @since 2.4.0 Improved error messages.
	 *
	 * @return array|WP_Error The merchant name or an error.
	 */
	public function get_latest_admin_interface(): array|WP_Error {
		$response = wp_remote_get(
			'https://api.github.com/repos/fe-data/fe-web-admin/releases',
			array(
				'headers' =>
					array(
						'Accept' => 'application/vnd.github+json',
					),
			)
		);
		if ( is_wp_error( $response ) ) {
			$this->logger->error( $response->get_error_message(), array( 'type' => 'Update Web Interface' ) );
			return( $response );
		}
		$http_code = (int) wp_remote_retrieve_response_code( $response );
		if ( 200 > $http_code || 299 < $http_code ) {
			$this->logger->error( wp_remote_retrieve_response_message( $response ), array( 'type' => 'Update Web Interface' ) );
			return new WP_Error( 'invalid_request', wp_remote_retrieve_response_message( $response ), array( 'status' => $http_code ) );
		}

		$result = json_decode( wp_remote_retrieve_body( $response ), true );
		if ( empty( $result ) ) {
			return new WP_Error( 'no_releases', __( 'No Management Interface releases found', 'fast-events' ), array( 'status' => 400 ) );
		}

		// Check for the right version.
		$index   = -1;
		$release = -1;
		foreach ( $result as $key => $value ) {
			if ( str_starts_with( $value['tag_name'], fast_events_func()::FE_ADMIN_VERSION ) ) {
				$tmp = (int) substr( $value['tag_name'], strlen( fast_events_func()::FE_ADMIN_VERSION ) + 1 ); // +1 is for the '.'.
				if ( $tmp > $release ) {
					$index   = $key;
					$release = $tmp;
				}
			}
		}

		// No release found.
		if ( -1 === $index ) {
			/* translators: 1: The release prefix  */
			return new WP_Error( 'no_release', sprintf( __( 'No Management Interface %1$s release found', 'fast-events' ), fast_events_func()::FE_ADMIN_VERSION ), array( 'status' => 400 ) );
		}

		return( $result[ $index ] );
	}


	/**
	 * Install/update Web interface.
	 *
	 * @since 2.0
	 * @since 2.0.3 Moved to uploads
	 * @since 2.5.0 Improved errors handling after failed download.
	 *
	 * @param int $type 1 = install, 2 = update, 3 = daily check.
	 *
	 * @return WP_Error|WP_REST_Response|null
	 */
	public function install_admin_interface( int $type ): WP_Error|WP_REST_Response|null {
		global $wp_filesystem;

		// If this the upgrade process and the management interface is not installed yet, return null.
		$mgt = $this->options->get_management_settings();
		if ( self::FE_ADMIN_UI_UPGRADE_PROCESS === $type && ! isset( $mgt['version'] ) ) {
			return null;
		}

		$upload_dir = wp_upload_dir();
		$fe_dir     = $upload_dir['basedir'] . '/fast-events';
		$path       = $upload_dir['basedir'] . '/fast-events/admin';
		$release    = $this->get_latest_admin_interface();
		if ( is_wp_error( $release ) ) {
			if ( self::FE_ADMIN_UI_UPGRADE_PROCESS === $type || self::FE_ADMIN_UI_DAILY_CHECK === $type ) {
				$this->logger->error( $release->get_error_message(), array( 'type' => 'Update Web Interface' ) );
				return null;
			} else {
				return $release;
			}
		}

		// Checks for the daily update check of the UI.
		if ( self::FE_ADMIN_UI_DAILY_CHECK === $type ) {
			$mgt['last_checked'] = current_time( 'mysql' );

			// Is there anything to update?
			if ( isset( $mgt['version'] ) && $mgt['version'] === $release['tag_name'] ) {
				$this->options->set_management_settings( $mgt );
				return null;
			}

			// Are we allowed to auto-update?
			// If not, just set the new version and changes.
			if ( ! isset( $mgt['auto_update'] ) || false === $mgt['auto_update'] ) {
				$mgt['new_changes'] = $release['body'];
				$mgt['new_version'] = $release['tag_name'];
				$this->options->set_management_settings( $mgt );
				return null;
			}
		}

		if ( ! file_exists( $fe_dir ) ) {
			$wp_filesystem->mkdir( $fe_dir );
		}
		$wp_filesystem->chdir( $fe_dir );

		// Get the zip-file, install it and remove it.
		if ( ! isset( $release['zipball_url'] ) ) {
			if ( self::FE_ADMIN_UI_SETTINGS_INSTALL === $type || self::FE_ADMIN_UI_DAILY_CHECK === $type ) {
				return new WP_Error( 'download_error', __( 'No download assets found', 'fast-events' ), array( 'status' => 404 ) );
			} else {
				return null;
			}
		}

		$zip_file = download_url( $release['zipball_url'] );
		if ( is_wp_error( $zip_file ) && ! $zip_file->get_error_data( 'softfail-filename' ) ) {
			if ( self::FE_ADMIN_UI_UPGRADE_PROCESS === $type || self::FE_ADMIN_UI_DAILY_CHECK === $type ) {
				$this->logger->error( sprintf( 'Download "%s" failed: ', $release['zipball_url'] ) . $zip_file->get_error_message(), array( 'type' => 'Update Web Interface' ) );
				return null;
			} else {
				/* translators: 1: GitHub release file URL */
				return new WP_Error( 'download_failed', sprintf( __( 'Download "%1$s" failed: ', 'fast-events' ), $release['zipball_url'] ) . $zip_file->get_error_message(), array( 'status' => 404 ) );
			}
		}

		if ( unzip_file( $zip_file, $fe_dir ) !== true ) {
			if ( self::FE_ADMIN_UI_UPGRADE_PROCESS === $type || self::FE_ADMIN_UI_DAILY_CHECK === $type ) {
				$this->logger->error( __( 'Can\'t unzip file', 'fast-events' ), array( 'type' => 'Update Web Interface' ) );
				return null;
			} else {
				return new WP_Error( 'cant_unzip', __( 'Can\'t unzip file', 'fast-events' ), array( 'status' => 404 ) );
			}
		}
		$wp_filesystem->delete( $zip_file );

		$dirlist = $wp_filesystem->dirlist( $fe_dir, false );
		foreach ( $dirlist as $dir ) {
			if ( str_starts_with( $dir['name'], 'fe-data-fe-web-admin' ) ) {
				$wp_filesystem->move( $fe_dir . '/' . $dir['name'], $path, true );
				break;
			}
		}

		// Check if it is an installation or update.
		$mgt = $this->options->get_management_settings();
		if ( empty( $mgt ) || ! isset( $mgt['installed'] ) ) {
			$mgt['installed'] = current_time( 'mysql' );
		}
		$mgt['last_checked'] = current_time( 'mysql' );
		$mgt['update']       = current_time( 'mysql' );
		$mgt['version']      = $release['tag_name'];
		$mgt['changes']      = $release['body'] ?? '';
		$mgt['new_changes']  = '';
		$mgt['new_version']  = '';
		if ( ! isset( $mgt['auto_update'] ) ) {
			$mgt['auto_update'] = false;
		}
		if ( self::FE_ADMIN_UI_DAILY_CHECK === $type ) {
			$mgt['new_changes'] = '';
			$mgt['new_version'] = '';
		}

		// Save the options.
		$this->options->set_management_settings( $mgt );

		// Check for a recurring action to check for an update of the web interface.
		if ( ! as_has_scheduled_action( 'fast_events_action_update_web' ) ) {
			as_schedule_recurring_action( time(), DAY_IN_SECONDS, 'fast_events_action_update_web' );
		}

		$this->logger->info( __( 'Installed version: ', 'fast-events' ) . $release['tag_name'], array( 'type' => 'Update Web Interface' ) );
		if ( self::FE_ADMIN_UI_UPGRADE_PROCESS === $type ) {
			return null;
		} else {
			return rest_ensure_response( __( 'Installed version: ', 'fast-events' ) . $release['tag_name'] );
		}
	}

	/**
	 * Delete the admin web interface.
	 *
	 * @since 3.0
	 * @return void
	 */
	public function delete_admin_interface(): void {
		global $wp_filesystem;

		// Remove the daily job.
		as_unschedule_all_actions( 'fast_events_action_update_web' );

		// Remove the directory of the management interface.
		$upload_dir = wp_upload_dir();
		$path       = $upload_dir['basedir'] . '/fast-events/admin';
		if ( $wp_filesystem->is_dir( $path ) ) {
			$wp_filesystem->delete( $path, true );
		}

		// Reset the options.
		$this->logger->info( __( 'Deleted the Web Interface', 'fast-events' ), array( 'type' => 'Delete Web Interface' ) );
		$this->options->set_management_settings( array() );
	}
}
