<?php

namespace Automattic\WooCommerce\Admin\PluginsInstallLoggers;

/**
 * A logger to log plugin installation progress in real time to an option.
 */
class AsyncPluginsInstallLogger implements PluginsInstallLogger {

	/**
	 * Variable to store logs.
	 *
	 * @var string $option_name option name to store logs.
	 */
	private $option_name;

	/**
	 * Constructor.
	 *
	 * @param string $option_name option name.
	 */
	public function __construct( string $option_name ) {
		$this->option_name = $option_name;
		add_option(
			$this->option_name,
			array(
				'created_time' => time(),
				'status'       => 'pending',
				'plugins'      => array(),
			),
			'',
			'no'
		);

		// Set status as failed in case we run out of execution time.
		register_shutdown_function(
			function () {
				$error = error_get_last();
				if ( isset( $error['type'] ) && E_ERROR === $error['type'] ) {
					$option           = $this->get();
					$option['status'] = 'failed';
					$this->update( $option );
				}
			}
		);
	}

	/**
	 * Update the option.
	 *
	 * @param array $data New data.
	 *
	 * @return bool
	 */
	private function update( array $data ) {
		return update_option( $this->option_name, $data );
	}

	/**
	 * Retrieve the option.
	 *
	 * @return false|mixed|void
	 */
	private function get() {
		return get_option( $this->option_name );
	}

	/**
	 * Add requested plugin.
	 *
	 * @param string $plugin_name plugin name.
	 *
	 * @return void
	 */
	public function install_requested( string $plugin_name ) {
		$option = $this->get();
		if ( ! isset( $option['plugins'][ $plugin_name ] ) ) {
			$option['plugins'][ $plugin_name ] = array(
				'status'           => 'installing',
				'errors'           => array(),
				'install_duration' => 0,
			);
		}
		$this->update( $option );
	}

	/**
	 * Add installed plugin.
	 *
	 * @param string $plugin_name plugin name.
	 * @param int    $duration time took to install plugin.
	 *
	 * @return void
	 */
	public function installed( string $plugin_name, int $duration ) {
		$option = $this->get();

		$option['plugins'][ $plugin_name ]['status']           = 'installed';
		$option['plugins'][ $plugin_name ]['install_duration'] = $duration;
		$this->update( $option );
	}

	/**
	 * Change status to activated.
	 *
	 * @param string $plugin_name plugin name.
	 *
	 * @return void
	 */
	public function activated( string $plugin_name ) {
		$option = $this->get();

		$option['plugins'][ $plugin_name ]['status'] = 'activated';
		$this->update( $option );
	}

	/**
	 * Add an error.
	 *
	 * @param string      $plugin_name plugin name.
	 * @param string|null $error_message error message.
	 *
	 * @return void
	 */
	public function add_error( string $plugin_name, ?string $error_message = null ) {
		$option = $this->get();

		$option['plugins'][ $plugin_name ]['errors'][] = $error_message;
		$option['plugins'][ $plugin_name ]['status']   = 'failed';
		$option['status']                              = 'failed';

		wc_admin_record_tracks_event(
			'coreprofiler_store_extension_installed_and_activated',
			array(
				'success'       => false,
				'extension'     => $this->get_plugin_track_key( $plugin_name ),
				'error_message' => $error_message,
			)
		);

		$this->update( $option );
	}

	/**
	 * Record completed_time.
	 *
	 * @param array $data return data from install_plugins().
	 * @return void
	 */
	public function complete( $data = array() ) {
		$option = $this->get();

		$option['complete_time'] = time();
		$option['status']        = 'complete';

		$this->track( $data );
		$this->update( $option );
	}

	private function get_plugin_track_key( $id ) {
		$slug = explode( ':', $id )[0];
		$key  = preg_match( '/^woocommerce(-|_)payments$/', $slug )
			? 'wcpay'
			: explode( ':', str_replace( '-', '_', $slug ) )[0];
		return $key;
	}

	/**
	 * Returns time frame for a given time in milliseconds.
	 *
	 * @param int $timeInMs - time in milliseconds
	 *
	 * @return string - Time frame.
	 */
	function get_timeframe( $timeInMs ) {
		$time_frames = array(
			array(
				'name' => '0-2s',
				'max'  => 2,
			),
			array(
				'name' => '2-5s',
				'max'  => 5,
			),
			array(
				'name' => '5-10s',
				'max'  => 10,
			),
			array(
				'name' => '10-15s',
				'max'  => 15,
			),
			array(
				'name' => '15-20s',
				'max'  => 20,
			),
			array(
				'name' => '20-30s',
				'max'  => 30,
			),
			array(
				'name' => '30-60s',
				'max'  => 60,
			),
			array( 'name' => '>60s' ),
		);

		foreach ( $time_frames as $time_frame ) {
			if ( ! isset( $time_frame['max'] ) ) {
				return $time_frame['name'];
			}
			if ( $timeInMs < $time_frame['max'] * 1000 ) {
				return $time_frame['name'];
			}
		}
	}

	private function track( $data ) {
		$track_data = array(
			'success'              => true,
			'installed_extensions' => array_map(
				function ( $extension ) {
					return $this->get_plugin_track_key( $extension );
				},
				$data['installed']
			),
			'total_time'           => $this->get_timeframe( ( time() - $data['start_time'] ) * 1000 ),
		);

		foreach ( $data['installed'] as $plugin ) {
			if ( ! isset( $data['time'][ $plugin ] ) ) {
				continue;
			}

			$plugin_track_key                                  = $this->get_plugin_track_key( $plugin );
			$install_time                                      = $this->get_timeframe( $data['time'][ $plugin ] );
			$track_data[ 'install_time_' . $plugin_track_key ] = $install_time;

			wc_admin_record_tracks_event(
				'coreprofiler_store_extension_installed_and_activated',
				array(
					'success'      => true,
					'extension'    => $plugin_track_key,
					'install_time' => $install_time,
				)
			);
		}

		wc_admin_record_tracks_event( 'coreprofiler_store_extensions_installed_and_activated', $track_data );
	}
}
