<?php
/**
 * Admin panel.
 *
 * Renders a lightweight (no-React) dashboard where the user can enable or
 * disable each element. Saved via a standard nonce-protected form POST.
 *
 * @package CraftAddonsForWPBakery
 */

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

/**
 * Class CAFW_Admin
 */
class CAFW_Admin {

	/**
	 * Admin page slug.
	 *
	 * @var string
	 */
	const PAGE = 'craft-addons-for-wpbakery';

	/**
	 * Nonce action.
	 *
	 * @var string
	 */
	const NONCE = 'cafw_save_elements';

	/**
	 * Loader reference, used to list elements.
	 *
	 * @var CAFW_Loader
	 */
	private $loader;

	/**
	 * Constructor.
	 *
	 * @param CAFW_Loader $loader Element loader.
	 */
	public function __construct( CAFW_Loader $loader ) {
		$this->loader = $loader;
	}

	/**
	 * Hook into the admin.
	 *
	 * @return void
	 */
	public function init() {
		add_action( 'admin_menu', array( $this, 'add_menu' ) );
		add_action( 'admin_post_cafw_save_elements', array( $this, 'handle_save' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) );
		add_action( 'in_admin_header', array( $this, 'suppress_foreign_notices' ), PHP_INT_MAX );
		add_filter( 'plugin_action_links_' . CAFW_BASENAME, array( $this, 'action_links' ) );
	}

	/**
	 * Whether the current request is rendering this plugin's settings page.
	 *
	 * @return bool
	 */
	private function is_plugin_screen() {
		$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;

		return $screen instanceof WP_Screen && 'toplevel_page_' . self::PAGE === $screen->id;
	}

	/**
	 * Remove admin notices registered by other plugins/themes on our page.
	 *
	 * Keeps this page focused on its own messaging. Our own notices live in the
	 * dashboard template (echoed directly, not via the admin_notices hooks), so
	 * they are unaffected. Runs in in_admin_header, which fires before the
	 * admin_notices / all_admin_notices hooks are output.
	 *
	 * @return void
	 */
	public function suppress_foreign_notices() {
		if ( ! $this->is_plugin_screen() ) {
			return;
		}

		remove_all_actions( 'admin_notices' );
		remove_all_actions( 'all_admin_notices' );
		remove_all_actions( 'user_admin_notices' );
		remove_all_actions( 'network_admin_notices' );
	}

	/**
	 * Add the top-level menu page.
	 *
	 * @return void
	 */
	public function add_menu() {
		// The brand mark as a data-URI SVG. WordPress fades it to ~60% when the
		// menu item is inactive and full white when active, matching native icons.
		$icon = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzAwIiBoZWlnaHQ9IjMwMCIgdmlld0JveD0iMCAwIDMwMCAzMDAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik0yMzQuMTQ2IDBDMjcwLjUxNiA4LjI0NjVlLTA1IDMwMCAyOS40ODM3IDMwMCA2NS44NTM1VjIzNC4xNDZDMzAwIDI3MC41MTYgMjcwLjUxNiAzMDAgMjM0LjE0NiAzMDBINjUuODUzNUMyOS40ODM3IDMwMCA3LjQyMjgyZS0wNSAyNzAuNTE2IDAgMjM0LjE0NlY2NS44NTM1QzguMjQ3NThlLTA1IDI5LjQ4MzcgMjkuNDgzNyA3LjgzNDE4ZS0wNSA2NS44NTM1IDBIMjM0LjE0NlpNODAuNDg3MyA2NS44NTM1Qzc1LjQzNjEgNjUuODUzOCA3MS4zNDE4IDY5Ljk0ODggNzEuMzQxOCA3NVYyMjVDNzEuMzQxOCAyMzAuMDUxIDc1LjQzNjEgMjM0LjE0NiA4MC40ODczIDIzNC4xNDZIMTk1LjczMUMyMDAuNzgzIDIzNC4xNDYgMjA0Ljg3OCAyMzAuMDUxIDIwNC44NzggMjI1VjIxMi4xOTVDMjA0Ljg3OCAyMDcuMTQ0IDIwMC43ODMgMjAzLjA0OSAxOTUuNzMxIDIwMy4wNDlIMTY4LjI5M0MxNjUuMjYyIDIwMy4wNDkgMTYyLjgwNSAyMDAuNTkxIDE2Mi44MDUgMTk3LjU2MVYxODguNDE1QzE2Mi44MDUgMTg1LjM4NCAxNjAuMzQ4IDE4Mi45MjcgMTU3LjMxNyAxODIuOTI3SDEyOC4wNDlDMTI1LjAxOCAxODIuOTI3IDEyMi41NjEgMTgwLjQ3IDEyMi41NjEgMTc3LjQzOVYxMjAuNzMxQzEyMi41NjEgMTE3LjcwMSAxMjUuMDE4IDExNS4yNDQgMTI4LjA0OSAxMTUuMjQ0SDE5NS43MzFDMjAwLjc4MyAxMTUuMjQ0IDIwNC44NzggMTExLjE0OSAyMDQuODc4IDEwNi4wOThWNzVDMjA0Ljg3OCA2OS45NDg2IDIwMC43ODMgNjUuODUzNSAxOTUuNzMxIDY1Ljg1MzVIODAuNDg3M1pNMTgxLjA5OCAxMzUuMzY2QzE3Ny4wNTcgMTM1LjM2NiAxNzMuNzggMTM4LjY0MiAxNzMuNzggMTQyLjY4M1YxODIuOTI3QzE3My43OCAxODYuOTY4IDE3Ny4wNTcgMTkwLjI0NCAxODEuMDk4IDE5MC4yNDRIMjIxLjM0MkMyMjUuMzgzIDE5MC4yNDQgMjI4LjY1OCAxODYuOTY4IDIyOC42NTggMTgyLjkyN1YxNDIuNjgzQzIyOC42NTggMTM4LjY0MiAyMjUuMzgzIDEzNS4zNjYgMjIxLjM0MiAxMzUuMzY2SDE4MS4wOThaIiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K';

		add_menu_page(
			esc_html__( 'Craft Addons', 'craft-addons-for-wpbakery' ),
			esc_html__( 'Craft Addons', 'craft-addons-for-wpbakery' ),
			'manage_options',
			self::PAGE,
			array( $this, 'render_page' ),
			$icon,
			58
		);

		add_submenu_page(
			self::PAGE,
			esc_html__( 'Dashboard', 'craft-addons-for-wpbakery' ),
			esc_html__( 'Dashboard', 'craft-addons-for-wpbakery' ),
			'manage_options',
			self::PAGE,
			array( $this, 'render_page' )
		);
	}

	/**
	 * Add a "Settings" link on the Plugins screen.
	 *
	 * @param string[] $links Existing action links.
	 * @return string[]
	 */
	public function action_links( $links ) {
		$url  = admin_url( 'admin.php?page=' . self::PAGE );
		$link = '<a href="' . esc_url( $url ) . '">' . esc_html__( 'Settings', 'craft-addons-for-wpbakery' ) . '</a>';
		array_unshift( $links, $link );
		return $links;
	}

	/**
	 * Enqueue admin assets only on our page.
	 *
	 * @param string $hook Current admin page hook.
	 * @return void
	 */
	public function enqueue( $hook ) {
		if ( 'toplevel_page_' . self::PAGE !== $hook ) {
			return;
		}

		wp_enqueue_style(
			'cafw-admin',
			CAFW_URL . 'admin/css/cafw-admin.css',
			array(),
			CAFW_Plugin::asset_ver( 'admin/css/cafw-admin.css' )
		);

		wp_enqueue_script(
			'cafw-dashboard',
			CAFW_URL . 'admin/js/cafw-dashboard.js',
			array(),
			CAFW_Plugin::asset_ver( 'admin/js/cafw-dashboard.js' ),
			true
		);
	}

	/**
	 * Handle the settings form submission.
	 *
	 * @return void
	 */
	public function handle_save() {
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_die( esc_html__( 'You are not allowed to do this.', 'craft-addons-for-wpbakery' ) );
		}

		check_admin_referer( self::NONCE );

		// Free elements and unlocked Pro elements honour the enable/disable toggle.
		// Locked Pro elements (no licence) are not toggleable, so they stay out of
		// the tracked set.
		$pro_unlocked = CAFW_Loader::pro_unlocked();
		$toggleable   = array();
		foreach ( $this->loader->get_elements() as $slug => $element ) {
			if ( ! $element->is_pro() || $pro_unlocked ) {
				$toggleable[] = $slug;
			}
		}

		$submitted = isset( $_POST['cafw_elements'] ) && is_array( $_POST['cafw_elements'] )
			? array_map( 'sanitize_key', wp_unslash( $_POST['cafw_elements'] ) )
			: array();

		$enabled = array_values( array_intersect( $toggleable, $submitted ) );

		CAFW_Settings::save_enabled( $enabled, $toggleable );

		$module_keys       = array_keys( CAFW_Modules::all() );
		$submitted_modules = isset( $_POST['cafw_modules'] ) && is_array( $_POST['cafw_modules'] )
			? array_map( 'sanitize_key', wp_unslash( $_POST['cafw_modules'] ) )
			: array();
		CAFW_Modules::save_enabled( array_values( array_intersect( $module_keys, $submitted_modules ) ), $module_keys );

		wp_safe_redirect(
			add_query_arg(
				array(
					'page'    => self::PAGE,
					'updated' => 'true',
				),
				admin_url( 'admin.php' )
			)
		);
		exit;
	}

	/**
	 * Metadata for Pro elements so the dashboard can advertise them as locked
	 * cards even in the free build, where the /pro folder (and its classes) is
	 * stripped out. Keyed by element slug.
	 *
	 * @return array<string,array{label:string,description:string,icon:string}>
	 */
	public static function pro_catalog() {
		return array(
			'image-scroller' => array(
				'label'       => __( 'Image Scroller', 'craft-addons-for-wpbakery' ),
				'description' => __( 'Screenshot that scrolls on hover.', 'craft-addons-for-wpbakery' ),
				'icon'        => 'dashicons-cover-image',
			),
			'before-after'   => array(
				'label'       => __( 'Before / After', 'craft-addons-for-wpbakery' ),
				'description' => __( 'Drag to compare two images.', 'craft-addons-for-wpbakery' ),
				'icon'        => 'dashicons-image-flip-horizontal',
			),
			'query-grid'     => array(
				'label'       => __( 'Query Grid', 'craft-addons-for-wpbakery' ),
				'description' => __( 'Lay out any content as a grid.', 'craft-addons-for-wpbakery' ),
				'icon'        => 'dashicons-grid-view',
			),
		);
	}

	/**
	 * Build the list of cards shown on the dashboard: every discovered element
	 * plus any Pro teasers not present in this build. Pro cards are "locked"
	 * until a valid licence unlocks them.
	 *
	 * @return array<string,array> Card data keyed by slug.
	 */
	private function get_cards() {
		$unlocked = CAFW_Loader::pro_unlocked();
		$cards    = array();

		foreach ( $this->loader->get_elements() as $slug => $element ) {
			$is_pro          = $element->is_pro();
			$locked          = $is_pro && ! $unlocked;
			$cards[ $slug ] = array(
				'label'       => $element->get_label(),
				'description' => $element->get_description(),
				'icon'        => $element->get_admin_icon(),
				'is_pro'      => $is_pro,
				'locked'      => $locked,
				// Free and unlocked Pro elements honour their saved toggle; a
				// locked Pro element shows as off until a licence unlocks it.
				'enabled'     => $locked ? false : CAFW_Settings::is_element_enabled( $slug ),
			);
		}

		if ( ! $unlocked ) {
			foreach ( self::pro_catalog() as $slug => $meta ) {
				if ( isset( $cards[ $slug ] ) ) {
					continue;
				}
				$cards[ $slug ] = array(
					'label'       => $meta['label'],
					'description' => $meta['description'],
					'icon'        => $meta['icon'],
					'is_pro'      => true,
					'locked'      => true,
					'enabled'     => false,
				);
			}
		}

		return $cards;
	}

	/**
	 * @return array<string,array> Module cards keyed by key.
	 */
	private function get_modules() {
		$unlocked = CAFW_Loader::pro_unlocked();
		$modules  = array();

		foreach ( CAFW_Modules::all() as $key => $module ) {
			$is_pro          = ! empty( $module['is_pro'] );
			$modules[ $key ] = array(
				'label'       => $module['label'],
				'description' => $module['description'],
				'icon'        => $module['icon'],
				'is_pro'      => $is_pro,
				'locked'      => false,
				'enabled'     => CAFW_Modules::is_enabled( $key ),
			);
		}

		if ( ! $unlocked ) {
			foreach ( CAFW_Modules::pro_catalog() as $key => $meta ) {
				if ( isset( $modules[ $key ] ) ) {
					continue;
				}
				$modules[ $key ] = array(
					'label'       => $meta['label'],
					'description' => $meta['description'],
					'icon'        => $meta['icon'],
					'is_pro'      => true,
					'locked'      => true,
					'enabled'     => false,
				);
			}
		}

		return $modules;
	}

	/**
	 * Render the dashboard page.
	 *
	 * @return void
	 */
	public function render_page() {
		if ( ! current_user_can( 'manage_options' ) ) {
			return;
		}

		$cafw_cards          = $this->get_cards();
		$cafw_modules        = $this->get_modules();
		$cafw_wpbakery_ready = cafw()->is_wpbakery_active();
		$cafw_updated        = isset( $_GET['updated'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only flag.
		$cafw_upgrade_url    = ( function_exists( 'cafw_fs' ) && method_exists( cafw_fs(), 'get_upgrade_url' ) ) ? cafw_fs()->get_upgrade_url() : '';

		require CAFW_PATH . 'admin/views/dashboard.php';
	}
}
