<?php
/**
 * Author: Rymera Web Co
 *
 * @package SAAI
 */

namespace SAAI;

use SAAI\Actions\Activation;
use SAAI\Actions\Deactivation;
use SAAI\Classes\Admin_Menu_Handler;
use SAAI\Helpers\Connect;
use SAAI\Helpers\Helper;
use SAAI\Traits\Singleton_Trait;

defined( 'ABSPATH' ) || exit;

require_once SAAI_PLUGIN_DIR_PATH . 'includes/autoload.php';

/**
 * Class App
 */
class App {

    use Singleton_Trait;

    /**
     * Holds the class instance object
     *
     * @since 1.0.0
     * @var App $instance object
     */
    protected static $instance;

    /**
     * Holds the class object instances.
     *
     * @var array An array of object class instance.
     */
    protected $objects;

    /**
     * App constructor.
     */
    public function __construct() {

        $this->objects = array();
    }

    /**
     * Called at the end of file to initialize autoloader
     */
    public function boot() {

        register_activation_hook( SAAI_PLUGIN_FILE, array( $this, 'activation_actions' ) );
        register_deactivation_hook( SAAI_PLUGIN_FILE, array( $this, 'deactivation_actions' ) );

        add_action( 'activated_plugin', array( $this, 'maybe_redirect_to_connect' ) );

        // Add version check on init with priority after text domain loading (which uses 10).
        add_action( 'init', array( $this, 'check_version' ), 20 );

        /***************************************************************************
         * Run the plugin
         ***************************************************************************
         *
         * We run the plugin classes on `setup_theme` hook with priority 100 as
         * we depend on WooCommerce plugin to be loaded first and we need to make
         * sure that WP_Rewrite global object is already available.
         */
        add_action( 'setup_theme', array( $this, 'run' ), 100 );

        /***************************************************************************
         * Maybe add HTML5 support
         ***************************************************************************
         *
         * We need HTML5 support for the theme in order for the newer script tag
         * attributes to work (_i.e._ `type="module"`).
         */
        add_action( 'after_setup_theme', array( $this, 'maybe_add_html5_support' ), 999 );

        /***************************************************************************
         * Add generator meta tag
         ***************************************************************************
         *
         * Add a generator meta tag to the HTML head to indicate StoreAgent is in use.
         * This helps tools like BuiltWith and Wappalyzer detect the plugin usage.
         */
        add_action( 'wp_head', array( $this, 'output_generator_meta_tag' ), 1 );
    }

    /**
     * Enables HTML5 support for the theme if not already. We require this in order for the newer script tag
     * attributes to work (_i.e._ `type="module"`).
     *
     * @since 1.0.0
     * @return void
     */
    public function maybe_add_html5_support() {

        if ( current_theme_supports( 'html5', 'script' ) ) {
            return;
        }

        add_theme_support( 'html5', array( 'script' ) );
    }

    /**
     * Register classes to run.
     *
     * @param array $objects Array of class instances.
     *
     * @return void
     */
    public function register_objects( $objects ) {

        $this->objects = array_merge( $this->objects, $objects );
    }

    /**
     * Plugin activation actions
     *
     * @param bool $sitewide Whether the plugin is being activated network-wide.
     */
    public function activation_actions( $sitewide ) {

        /***************************************************************************
         * Plugin activation actions
         ***************************************************************************
         *
         * We run the plugin actions here when it's activated.
         */
        ( new Activation( $sitewide ) )->run();

        flush_rewrite_rules();
    }

    /**
     * Redirect to connect page after plugin activation.
     *
     * @param string $plugin The plugin file path.
     *
     * @return void
     */
    public function maybe_redirect_to_connect( $plugin ) {

        /***************************************************************************
         * We check if the plugin is activated via WP-CLI or CLI.
         ***************************************************************************
         *
         * We check if the plugin is activated via WP-CLI or CLI and just output a
         * message to the console to connect the plugin via a connect URL.
         */
        if ( ( ( defined( 'WP_CLI' ) && WP_CLI ) || php_sapi_name() === 'cli' ) ) {
            if ( plugin_basename( SAAI_PLUGIN_FILE ) === $plugin && ! Connect::is_connected() && class_exists( '\WP_CLI' ) ) {
                \WP_CLI::log(
                    sprintf(
                        'Please connect the plugin by visiting the following URL: %s',
                        esc_url( Connect::get_url() )
                    )
                );
            }

            return;
        }

        /***************************************************************************
         * Handle plugin activation via plugins.php Bulk Actions
         ***************************************************************************
         *
         * We check if the plugin is activated via plugins.php Bulk Actions and
         * redirect to the connect page if it's the only plugin activated. Otherwise,
         * we do nothing.
         */
        if ( ! empty( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'] ) {
            $definition = array(
                'checked' => array(
                    'filter' => FILTER_SANITIZE_FULL_SPECIAL_CHARS,
                    'flags'  => FILTER_REQUIRE_ARRAY,
                ),
                'action'  => FILTER_SANITIZE_FULL_SPECIAL_CHARS,
                'action2' => FILTER_SANITIZE_FULL_SPECIAL_CHARS,
            );

            $posted = filter_input_array( INPUT_POST, $definition );
            if ( $posted &&
                ( isset( $posted['checked'] ) && count( $posted['checked'] ) === 1 ) &&
                ( isset( $posted['action'] ) && 'activate-selected' === $posted['action'] ) &&
                ( isset( $posted['action2'] ) && 'activate-selected' === $posted['action2'] ) &&
                plugin_basename( SAAI_PLUGIN_FILE ) === array_shift( $posted['checked'] ) &&
                ! Connect::is_connected() ) {
                wp_safe_redirect( admin_url( 'admin.php?page=' . Admin_Menu_Handler::PARENT_SLUG ) );
                exit;
            }
        } elseif ( plugin_basename( SAAI_PLUGIN_FILE ) === $plugin && ! Connect::is_connected() ) {
            wp_safe_redirect( admin_url( 'admin.php?page=' . Admin_Menu_Handler::PARENT_SLUG ) );
            exit;
        }
    }

    /**
     * Run the plugin classes.
     *
     * @return void
     */
    public function run() {

        // Register the REST API permission filter.
        add_filter( 'woocommerce_rest_is_request_to_rest_api', array( $this, 'woocommerce_rest_is_request_to_rest_api' ), 10 );

        /***************************************************************************
         * We make sure that the classes to be run extends the abstract class.
         ***************************************************************************
         *
         * We make sure that the classes to be run extends the abstract class or has
         * implemented a `run` method.
         */
        foreach ( $this->objects as $object ) {

            /*****************************************************************************
             * If the object is a subclass of Abstract_CLI, add it to the WP_CLI commands.
             *****************************************************************************
             *
             * We make sure that the classes to be run extends the abstract class or has
             * implemented a `run` method.
             */
            if ( is_subclass_of( $object, 'SAAI\Abstracts\Abstract_CLI' ) &&
                defined( 'WP_CLI' ) && WP_CLI ) {
                \WP_CLI::add_command( $object->command(), $object );
                continue;
            }

            if ( ! method_exists( $object, 'run' ) ) {
                _doing_it_wrong(
                    __METHOD__,
                    'The ' . get_class( $object ) . ' does not have a run method. Please make sure to extend the Abstract_Class class.', // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
                    esc_html( SAAI_VERSION )
                );
                continue;
            }
            $class_object = strtolower( wp_basename( get_class( $object ) ) );

            $this->objects[ $class_object ] = apply_filters(
                'saai_class_object',
                $object,
                $class_object,
                $this
            );
            $this->objects[ $class_object ]->run();
        }
    }

    /**
     * Plugin deactivation actions
     *
     * @param bool $sitewide Whether the plugin is being deactivated network-wide.
     */
    public function deactivation_actions( $sitewide ) {

        /***************************************************************************
         * Plugin deactivation actions
         ***************************************************************************
         *
         * We run the plugin actions here when it's deactivated.
         */
        ( new Deactivation( $sitewide ) )->run();

        flush_rewrite_rules();
    }

    /**
     * Check if the plugin version has changed and run necessary updates.
     *
     * @since 1.0.5
     * @return void
     */
    public function check_version() {
        $stored_version = get_option( SAAI_INSTALLED_VERSION );

        // If stored version doesn't exist, or current version is higher than stored version.
        if ( ! $stored_version || version_compare( SAAI_VERSION, $stored_version, '>' ) ) {
            // Run the database table creation.
            $activation = new Activation();
            $activation->run();

            // Update the stored version.
            update_option( SAAI_INSTALLED_VERSION, SAAI_VERSION );
        }
    }

    /**
     * Allow our API endpoints permission to be processed by WC REST API authentication scripts.
     *
     * @param bool $is_request_to_rest_api True if the request is to the REST API, false otherwise.
     * @return bool True if the request is to the REST API, false otherwise.
     */
    public function woocommerce_rest_is_request_to_rest_api( $is_request_to_rest_api ) {
        $request_uri = esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput

        if ( strpos( $request_uri, '/storeagent-ai/v' ) !== false ) {
            return true;
        }

        return $is_request_to_rest_api;
    }

    /**
     * Output the generator meta tag in the HTML head.
     *
     * @since 1.1.2
     * @return void
     */
    public function output_generator_meta_tag() {
        $plugin_name    = Helper::get_plugin_data( 'Name' );
        $plugin_version = SAAI_VERSION;

        if ( empty( $plugin_name ) || empty( $plugin_version ) ) {
            return;
        }

        $generator_content = sprintf(
            '%s v%s',
            esc_attr( $plugin_name ),
            esc_attr( $plugin_version )
        );

        printf(
            '<meta name="generator" content="%s" />%s',
            esc_attr( $generator_content ),
            "\n"
        );
    }
}

/***************************************************************************
 * Instantiate classes
 ***************************************************************************
 *
 * Instantiate classes to be registered and run.
 */
App::instance()->register_objects(
    array_merge(
        require_once SAAI_PLUGIN_DIR_PATH . 'bootstrap/rest-objects.php',
        require_once SAAI_PLUGIN_DIR_PATH . 'bootstrap/class-objects.php',
        require_once SAAI_PLUGIN_DIR_PATH . 'bootstrap/integration-objects.php',
        require_once SAAI_PLUGIN_DIR_PATH . 'bootstrap/ai-objects.php',
        require_once SAAI_PLUGIN_DIR_PATH . 'bootstrap/automation-objects.php',
        require_once SAAI_PLUGIN_DIR_PATH . 'bootstrap/cli-objects.php',
        require_once SAAI_PLUGIN_DIR_PATH . 'bootstrap/third-party-objects.php',
    )
);

return App::instance();
