<?php
/**
 * File containing the class \Crowdsignal_Forms\Crowdsignal_Forms.
 *
 * @package Crowdsignal_Forms
 * @since   0.9.0
 */

namespace Crowdsignal_Forms;

use Crowdsignal_Forms\Frontend\Crowdsignal_Forms_Blocks_Assets;
use Crowdsignal_Forms\Frontend\Crowdsignal_Forms_Blocks;
use Crowdsignal_Forms\Gateways\Api_Gateway_Interface;
use Crowdsignal_Forms\Gateways\Api_Gateway;
use Crowdsignal_Forms\Gateways\Post_Poll_Meta_Gateway;
use Crowdsignal_Forms\Gateways\Post_Survey_Meta_Gateway;
use Crowdsignal_Forms\Logging\Webservice_Logger;
use Crowdsignal_Forms\Rest_Api\Controllers\Nps_Controller;
use Crowdsignal_Forms\Rest_Api\Controllers\Feedback_Controller;
use Crowdsignal_Forms\Rest_Api\Controllers\Polls_Controller;
use Crowdsignal_Forms\Rest_Api\Controllers\Account_Controller;
use Crowdsignal_Forms\Admin\Admin_Hooks;
use Crowdsignal_Forms\Admin\Crowdsignal_Forms_Admin_Notices;
use Crowdsignal_Forms\Auth\Crowdsignal_Forms_Api_Authenticator;

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

/**
 * Main Crowdsignal Forms class.
 *
 * @class Crowdsignal_Forms
 */
final class Crowdsignal_Forms {

	/**
	 * Instance of class.
	 *
	 * @var Crowdsignal_Forms
	 */
	private static $instance;

	/**
	 * The plugin dir.
	 *
	 * @var string
	 */
	private $plugin_dir;

	/**
	 * The plugin url.
	 *
	 * @var string
	 */
	private $plugin_url;

	/**
	 * Our textdomain.
	 *
	 * @var string
	 */
	private $plugin_textdomain = 'crowdsignal-forms';

	/**
	 * Blocks registry.
	 *
	 * @var Crowdsignal_Forms_Blocks
	 */
	private $blocks;

	/**
	 * The polls controller.
	 *
	 * @var Polls_Controller
	 */
	public $rest_api_polls_controller;

	/**
	 * The nps controller.
	 *
	 * @var Nps_Controller
	 */
	public $rest_api_nps_controller;

	/**
	 * The feedback controller.
	 *
	 * @var Feedback_Controller
	 */
	public $rest_api_feedback_controller;

	/**
	 * The api gateway.
	 *
	 * @var Api_Gateway_Interface
	 */
	private $api_gateway = null;

	/**
	 * The admin hooks instance.
	 *
	 * @var Admin_Hooks
	 */
	private $admin_hooks;

	/**
	 * For saving/updating poll data from the api into post meta.
	 *
	 * @since 0.9.0
	 * @var Post_Poll_Meta_Gateway
	 */
	private $post_poll_meta_gateway = null;

	/**
	 * For saving/updating survey data (NPS/Feedback) from the api into post meta.
	 *
	 * @since 1.8.0
	 * @var Post_Survey_Meta_Gateway
	 */
	private $post_survey_meta_gateway = null;

	/**
	 * The logger we use to record our webservice conversations.
	 *
	 * @since 0.9.0
	 * @var null|Webservice_Logger
	 */
	private $webservice_logger;

	/**
	 * For account actions.
	 *
	 * @since 0.9.0
	 * @var Account_Controller
	 */
	private $rest_api_account_controller;

	/**
	 * Registers the block assets needed.
	 *
	 * @since 0.9.0
	 * @var Crowdsignal_Forms_Blocks_Assets
	 */
	private $blocks_assets;

	/**
	 * The instance of the api authenticator.
	 *
	 * @since 1.0.0
	 * @var Crowdsignal_Forms_Api_Authenticator|null
	 */
	private $api_authenticator;

	/**
	 * Initialize the singleton instance.
	 *
	 * @since 0.9.0
	 */
	private function __construct() {
		$this->plugin_dir = dirname( __DIR__ );
		$this->plugin_url = untrailingslashit( plugins_url( '', CROWDSIGNAL_FORMS_PLUGIN_BASENAME ) );

		add_action( 'admin_init', array( $this, 'activate_redirect' ) );
		register_deactivation_hook( CROWDSIGNAL_FORMS_PLUGIN_FILE, array( $this, 'deactivation' ) );
		register_activation_hook( CROWDSIGNAL_FORMS_PLUGIN_FILE, array( $this, 'activate' ) );
	}

	/**
	 * Fetches an instance of the class.
	 *
	 * @return self
	 */
	public static function instance() {
		if ( ! self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Run when plugin is activated
	 *
	 * @since 0.9.0
	 */
	public function activate() {
		Crowdsignal_Forms_Admin_Notices::add_notice( Crowdsignal_Forms_Admin_Notices::NOTICE_CORE_SETUP );
		add_option( 'crowdsignal_forms_do_activation_redirect', true );
	}

	/**
	 * Performs a redirect to the getting started page.
	 *
	 * @since 0.9.0
	 */
	public function activate_redirect() {
		if ( get_option( 'crowdsignal_forms_do_activation_redirect', false ) ) {
			delete_option( 'crowdsignal_forms_do_activation_redirect' );

			// If a user code is set, skip the redirect.
			if ( get_option( Crowdsignal_Forms_Api_Authenticator::USER_CODE_NAME ) ) {
				return;
			}

			wp_safe_redirect( admin_url( 'options-general.php?page=crowdsignal-settings' ) );
			exit();
		}
	}

	/**
	 * Clean up on deactivation.
	 *
	 * @since 0.9.0
	 */
	public function deactivation() {
	}

	/**
	 * Includes all php files needed and sets all the objects this class will use for initializing.
	 *
	 * @since 0.9.0
	 *
	 * @return $this
	 */
	public function bootstrap() {
		$this->blocks                       = new Crowdsignal_Forms_Blocks();
		$this->blocks_assets                = new Crowdsignal_Forms_Blocks_Assets();
		$this->rest_api_account_controller  = new Account_Controller();
		$this->rest_api_nps_controller      = new Nps_Controller();
		$this->rest_api_feedback_controller = new Feedback_Controller();
		$this->rest_api_polls_controller    = new Polls_Controller();
		$this->admin_hooks                  = new Admin_Hooks();
		$this->webservice_logger            = new Webservice_Logger();

		return $this;
	}


	/**
	 * Setup all filters and hooks. For frontend and optionally, admin.
	 *
	 * @param bool $init_all Pass in `true` to load and initialize both frontend and admin functionality. `false` by default.
	 *
	 * @return $this
	 */
	public function setup_hooks( $init_all = false ) {

		add_action( 'init', array( $this->blocks_assets, 'register' ) );
		add_action( 'init', array( $this->blocks, 'register' ) );
		add_action( 'rest_api_init', array( $this, 'register_rest_api_routes' ) );

		add_filter( 'block_categories_all', array( $this, 'add_block_category' ), 10, 2 );
		add_filter( 'crowdsignal_forms_api_request_headers', array( $this, 'add_auth_request_headers' ) );

		$this->admin_hooks->hook();
		$this->webservice_logger->hook_defaults();

		/**
		 * Set any other hooks, passing this instance.
		 *
		 * @param Crowdsignal_Forms $instance The instance.
		 * @since 0.9.0
		 */
		do_action( 'crowdsignal_forms_after_setup_hooks', $this );

		return $this;
	}

	/**
	 * Registers all REST api routes.
	 *
	 * @since 0.9.0
	 */
	public function register_rest_api_routes() {
		$this->rest_api_account_controller->register_routes();
		$this->rest_api_nps_controller->register_routes();
		$this->rest_api_polls_controller->register_routes();
		$this->rest_api_feedback_controller->register_routes();

		/**
		 * Any additional controllers from companion plugins can be registered using this hook.
		 *
		 * @param object $this This plugin's bootstrap instance.
		 * @since 0.9.0
		 */
		do_action( 'crowdsignal_forms_register_rest_api_routes', $this );
	}

	/**
	 * Initializes the class and adds all filters and actions.
	 *
	 * @since 0.9.0
	 *
	 * @param bool $init_all Pass in `true` to load and initialize both frontend and admin functionality. `false` by default.
	 *
	 * @return self
	 */
	public static function init( $init_all = false ) {
		return self::instance()->bootstrap()->setup_hooks( $init_all );
	}

	/**
	 * Get the plugin dir.
	 *
	 * @since 0.9.0
	 *
	 * @return string
	 */
	public function get_plugin_dir() {
		return $this->plugin_dir;
	}

	/**
	 * Get the api gateway.
	 *
	 * @return Api_Gateway_Interface
	 */
	public function get_api_gateway() {
		if ( null === $this->api_gateway ) {
			$this->api_gateway = new Api_Gateway();
		}

		return $this->api_gateway;
	}


	/**
	 * Set the api gateway.
	 *
	 * @param Api_Gateway_Interface $gateway The gateway.
	 *
	 * @return $this
	 */
	public function set_api_gateway( $gateway ) {
		$this->api_gateway = $gateway;
		return $this;
	}

	/**
	 * Add API key and usercode to the API request headers.
	 *
	 * @param array $headers Any existing header values.
	 *
	 * @return $headers array the modified array.
	 */
	public function add_auth_request_headers( $headers ) {
		$cs_authenticator = $this->get_api_authenticator();
		$api_key          = $cs_authenticator->get_api_key();

		if ( empty( $api_key ) ) {
			return $headers;
		}

		// check if we have it already.
		$transient_key     = $api_key . '-' . (int) get_current_user_id();
		$transient_headers = get_transient( $transient_key );
		if ( $transient_headers ) {
			return array_merge( $headers, $transient_headers );
		}

		$auth_headers = array();
		$user_code    = $cs_authenticator->get_user_code();

		if ( ! empty( $user_code ) ) {
			$auth_headers['x-api-partner-guid'] = $api_key;
			$auth_headers['x-api-user-code']    = $user_code;
			set_transient( $transient_key, $auth_headers, MINUTE_IN_SECONDS );
		}

		return array_merge( $headers, $auth_headers );
	}

	/**
	 * Get the api gateway.
	 *
	 * @return Post_Poll_Meta_Gateway
	 */
	public function get_post_poll_meta_gateway() {
		if ( null === $this->post_poll_meta_gateway ) {
			$this->post_poll_meta_gateway = new Post_Poll_Meta_Gateway();
		}

		return $this->post_poll_meta_gateway;
	}

	/**
	 * Set the meta gateway.
	 *
	 * @since 1.0.0
	 *
	 * @param Post_Poll_Meta_Gateway $gateway The gateway.
	 *
	 * @return $this
	 */
	public function set_post_poll_meta_gateway( $gateway ) {
		$this->post_poll_meta_gateway = $gateway;
		return $this;
	}

	/**
	 * Get the survey meta gateway.
	 *
	 * @since 1.8.0
	 *
	 * @return Post_Survey_Meta_Gateway
	 */
	public function get_post_survey_meta_gateway() {
		if ( null === $this->post_survey_meta_gateway ) {
			$this->post_survey_meta_gateway = new Post_Survey_Meta_Gateway();
		}

		return $this->post_survey_meta_gateway;
	}

	/**
	 * Set the survey meta gateway.
	 *
	 * @since 1.8.0
	 *
	 * @param Post_Survey_Meta_Gateway $gateway The gateway.
	 *
	 * @return $this
	 */
	public function set_post_survey_meta_gateway( $gateway ) {
		$this->post_survey_meta_gateway = $gateway;
		return $this;
	}

	/**
	 * Get our webservice logger.
	 *
	 * @since 0.9.0
	 *
	 * @return Webservice_Logger
	 */
	public function get_webservice_logger() {
		return $this->webservice_logger;
	}

	/**
	 * Get the authenticator
	 *
	 * @since 1.0.0
	 *
	 * @return Crowdsignal_Forms_Api_Authenticator|null
	 */
	public function get_api_authenticator() {
		if ( null === $this->api_authenticator ) {
			$this->api_authenticator = new Crowdsignal_Forms_Api_Authenticator();
		}
		return $this->api_authenticator;
	}

	/**
	 * Set the authenticator
	 *
	 * @param Crowdsignal_Forms_Api_Authenticator $api_authenticator The authenticator to use.
	 *
	 * @since 1.0.0
	 *
	 * @return $this
	 */
	public function set_api_authenticator( $api_authenticator ) {
		$this->api_authenticator = $api_authenticator;
		return $this;
	}

	/**
	 * Adds the Crowdsignal block editor category.
	 *
	 * @param array   $categories Array of existing categories.
	 * @param WP_Post $post The post being edited.
	 */
	public function add_block_category( $categories, $post ) {
		return array_merge(
			$categories,
			array(
				array(
					'slug'  => 'crowdsignal-forms',
					'title' => __( 'Crowdsignal', 'crowdsignal-forms' ),
				),
			)
		);
	}
}