<?php
/**
 * KooKoa Folders — Main plugin class.
 *
 * Singleton bootstraps every sub-system: database, folders, AJAX, admin UI,
 * media integration, and dynamic folders. Kept in a dedicated file so the
 * plugin entry point does not mix function and class definitions
 * (Universal.Files.SeparateFunctionsFromOO).
 *
 * @package KookoaWPF
 */

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

/**
 * Main plugin class (singleton).
 *
 * Owns the bootstrap (dependencies + hook registration), the folder taxonomy,
 * the structural admin menu and a handful of cross-cutting helpers. The asset
 * enqueue, localized JS payload and commercial funnel surfaces are delegated
 * to per-domain collaborators (KookoaWPF_Assets, KookoaWPF_Localize,
 * KookoaWPF_Funnel) — but every hook stays registered here so external
 * references and registered callbacks never change.
 *
 * @since 1.0.0
 */
final class KookoaWPF {

	/**
	 * Singleton instance.
	 *
	 * @var self|null
	 */
	private static $instance = null;

	/**
	 * Return the singleton instance, creating it on first call.
	 *
	 * @return self
	 */
	public static function instance() {
		if ( is_null( self::$instance ) ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Constructor — loads files and registers hooks.
	 */
	private function __construct() {
		$this->load_dependencies();
		$this->init_hooks();
		// Smart Folders bootstrap: registers count-cache invalidation hooks that
		// must fire on both admin and front-end mutations (e.g. REST uploads),
		// so it cannot live inside admin_init.
		KookoaWPF_Dynamic::instance();
	}

	/**
	 * Require all class files.
	 */
	private function load_dependencies(): void {
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-database.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-postmeta-bulk.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-folders.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-dynamic.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-ajax-handler.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-ajax.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-ajax-items.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-ajax-meta.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-ajax-tree.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-ajax-dynamic.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-ajax-migration.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-ajax-folders.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-ajax-settings.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-ajax-download.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-admin.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-media.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-export.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-importer-abstract.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-importer-filebird-media.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-importer-realmedialibrary.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-importer-taxonomy.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-importer-registry.php';
		// Main-class collaborators — extracted from KookoaWPF and dispatched via
		// the delegators below (asset enqueue, localized JS payload, funnel).
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-assets.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-localize.php';
		require_once KOOKOAWPF_PLUGIN_DIR . 'includes/class-kookoawpf-funnel.php';
	}

	/**
	 * Register activation, deactivation, and runtime hooks.
	 */
	private function init_hooks(): void {
		register_activation_hook( KOOKOAWPF_PLUGIN_FILE, array( 'KookoaWPF_Database', 'activate' ) );
		register_deactivation_hook( KOOKOAWPF_PLUGIN_FILE, array( 'KookoaWPF_Database', 'deactivate' ) );
		add_action( 'plugins_loaded', array( $this, 'on_plugins_loaded' ) );
		// Late priority so every other plugin's CPTs are registered first —
		// guarantees get_post_types() returns the full set when the
		// kookoawpf_registered_post_types filter runs, and avoids "invalid
		// taxonomy" errors on AJAX from CPT edit screens.
		add_action( 'init', array( $this, 'register_taxonomy' ), 99 );
		add_action( 'admin_init', array( $this, 'init_components' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) );
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_builder_assets' ) );
		add_action(
			'elementor/editor/before_enqueue_scripts',
			function () {
				$this->enqueue_elementor_assets();
			}
		);

		// On frontend builder pages, render modal HTML via wp_footer.
		if ( ! is_admin() && $this->is_builder_editor() ) {
			add_action( 'wp_footer', array( $this, 'render_builder_modals' ) );
		}
		// Elementor editor runs inside wp-admin — use admin_footer.
		add_action( 'elementor/editor/footer', array( $this, 'render_builder_modals' ) );

		add_filter( 'admin_footer_text', array( $this, 'footer_credit_line' ), 20 );
		add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
		// Redirect away from the Upgrade page when something earlier in the
		// admin_menu chain (typically the PRO bootstrap's "no active PRO
		// access" branch) has stripped it out of `$submenu`. Without this
		// hook the user lands on WP's generic "Sorry, you are not allowed
		// to access this page" wp_die screen with no obvious next step.
		// We send them to the Freemius Account page instead — that's where
		// the "Activate License" form lives.
		add_action( 'admin_init', array( $this, 'maybe_redirect_blocked_upgrade' ), 5 );
		add_action( 'admin_head', array( $this, 'hide_upgrade_submenu_link' ) );
		add_filter( 'parent_file', array( $this, 'highlight_parent_on_upgrade' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_menu_styles' ) );
		add_filter( 'plugin_action_links_' . KOOKOAWPF_PLUGIN_BASENAME, array( $this, 'plugin_action_links' ), 20 );
		add_action( 'admin_notices', array( $this, 'render_dashboard_notices' ) );
	}

	/**
	 * Callback for 'plugins_loaded': run upgrades and register textdomain filter.
	 */
	public function on_plugins_loaded(): void {
		add_filter( 'load_textdomain_mofile', array( $this, 'filter_mofile' ), 10, 2 );

		KookoaWPF_Database::maybe_upgrade();
	}

	/**
	 * Decide which .mo file (if any) to load for this plugin.
	 *
	 * Three branches:
	 *  - Force English mode  → return '' so no translation file is loaded.
	 *  - Exact locale exists → return path unchanged.
	 *  - Otherwise           → try the generic language code bundled in the
	 *                          plugin (es_MX → es, fr_BE → fr, de_AT → de…).
	 *
	 * @param  string $mofile Path WordPress wants to load.
	 * @param  string $domain Text domain.
	 * @return string Path to load (empty string blocks loading).
	 */
	public function filter_mofile( $mofile, $domain ) {
		if ( 'kookoa-folders' !== $domain ) {
			return $mofile;
		}

		if ( get_option( 'kookoawpf_force_english', 'yes' ) === 'yes' ) {
			return '';
		}

		if ( file_exists( $mofile ) ) {
			return $mofile;
		}

		$base = basename( $mofile, '.mo' );
		if ( preg_match( '/^(.+-)([a-z]{2,3})_[A-Z]{2,4}$/', $base, $matches ) ) {
			$fallback = dirname( $mofile ) . '/' . $matches[1] . $matches[2] . '.mo';
			if ( file_exists( $fallback ) ) {
				return $fallback;
			}
		}

		return $mofile;
	}

	/**
	 * Register a private hierarchical taxonomy for each enabled post type.
	 */
	public function register_taxonomy(): void {
		foreach ( $this->get_supported_post_types() as $pt ) {
			$tax = 'kookoawpf_folder_' . sanitize_key( $pt );
			if ( taxonomy_exists( $tax ) ) {
				continue;
			}
			$args = array(
				'labels'            => array(
					'name'          => 'Folders',
					'singular_name' => 'Folder',
				),
				'public'            => false,
				'show_ui'           => false,
				'show_in_menu'      => false,
				'show_admin_column' => false,
				'hierarchical'      => true,
				'rewrite'           => false,
				'query_var'         => false,
				'show_in_rest'      => false,
			);
			// Use generic count callback so term counts update immediately.
			$args['update_count_callback'] = '_update_generic_term_count';
			register_taxonomy( $tax, $pt, $args );
		}
	}

	/**
	 * Get the post types supported by KooKoa Folders.
	 *
	 * Media Library only by default. Companion plugins can extend the list
	 * via the 'kookoawpf_registered_post_types' filter.
	 *
	 * @return string[] Array of post type slugs.
	 */
	public function get_supported_post_types() {
		$post_types = array( 'attachment' );
		/**
		 * Filter the post types supported by KooKoa Folders.
		 *
		 * @param string[] $post_types Default: array( 'attachment' ).
		 */
		return apply_filters( 'kookoawpf_registered_post_types', $post_types );
	}

	/**
	 * Get the CSS value for tree font size from settings.
	 */
	public function get_tree_font_size_css(): string {
		$map  = array(
			'compact' => '12px',
			'default' => '13px',
			'large'   => '14px',
			'xlarge'  => '15px',
		);
		$pref = get_option( 'kookoawpf_tree_font_size', 'default' );
		return isset( $map[ $pref ] ) ? $map[ $pref ] : '13px';
	}

	/**
	 * Instantiate admin sub-components (runs on 'admin_init').
	 */
	public function init_components(): void {
		KookoaWPF_Admin::instance();
		KookoaWPF_Ajax::instance();
		KookoaWPF_Ajax_Download::instance();
		KookoaWPF_Media::instance();
		KookoaWPF_Export::instance();
	}

	/**
	 * Detect if the current screen is a visual page-builder editor.
	 *
	 * When a builder is active we still load our JS/CSS (the media modal
	 * integration needs it), but we skip the sidebar, jstree, jQuery-UI
	 * drag/drop, and tell JS to skip full init.
	 *
	 * Public so the asset collaborator (KookoaWPF_Assets) can reuse it.
	 *
	 * @return bool
	 */
	public function is_builder_editor() {
		// Builder detection: look at QUERY_STRING directly rather than $_GET.
		// The check is purely read-only (we never mutate state from these
		// markers); it acts as a request-equivalent feature flag and
		// therefore does not require a nonce.
		$query_string = isset( $_SERVER['QUERY_STRING'] ) ? sanitize_text_field( wp_unslash( $_SERVER['QUERY_STRING'] ) ) : '';
		if ( '' === $query_string ) {
			return false;
		}
		$markers = array(
			'et_fb=',             // Divi 4 Visual Builder (front-end).
			'et_bfb=',            // Divi 4 Back-end Builder.
			'et-d5=',             // Divi 5 React builder.
			'divi=',              // Divi 5 alt marker.
			'action=elementor',   // Elementor editor.
			'elementor-preview=', // Elementor preview.
			'fl_builder=',        // Beaver Builder.
			'vc_editable=',       // WPBakery (Visual Composer).
		);
		foreach ( $markers as $marker ) {
			if ( false !== strpos( $query_string, $marker ) ) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Get file modification time for cache busting, with fallback to plugin version.
	 *
	 * Public so the asset collaborator (KookoaWPF_Assets) can reuse it.
	 *
	 * @param string $file Absolute file path.
	 * @return string
	 */
	public function file_ver( $file ): string {
		if ( file_exists( $file ) ) {
			$mtime = filemtime( $file );
			if ( false !== $mtime ) {
				return (string) $mtime;
			}
		}
		return KOOKOAWPF_VERSION;
	}

	/**
	 * Enqueue CSS and JS for admin screens that display folders.
	 * Delegates to the Assets collaborator.
	 *
	 * @param string $hook The current admin page hook suffix.
	 */
	public function enqueue_admin_assets( $hook ): void {
		KookoaWPF_Assets::instance()->enqueue_admin_assets( $hook );
	}

	/**
	 * Enqueue assets on frontend when a page builder is active.
	 * Delegates to the Assets collaborator.
	 */
	public function enqueue_builder_assets(): void {
		KookoaWPF_Assets::instance()->enqueue_builder_assets();
	}

	/**
	 * Enqueue assets specifically for the Elementor editor.
	 * Delegates to the Assets collaborator.
	 */
	public function enqueue_elementor_assets(): void {
		KookoaWPF_Assets::instance()->enqueue_elementor_assets();
	}

	/**
	 * Render builder modal JSON on frontend builder / Elementor editor pages.
	 * Delegates to the Localize collaborator.
	 */
	public function render_builder_modals(): void {
		KookoaWPF_Localize::instance()->render_builder_modals();
	}

	/**
	 * Register the settings page as a top-level admin menu.
	 *
	 * Keeps the structural Settings entry here; the commercial menu entries
	 * (hidden Upgrade pricing sub-page, Contact Us link, phase-aware CTA
	 * submenu) are delegated to KookoaWPF_Funnel, registered immediately
	 * after so the submenu order is preserved exactly.
	 */
	public function add_admin_menu(): void {
		// SVG folder icon (closed, 20×20) passed as a plain UTF-8 data URI
		// via rawurlencode(); no base64 encoding needed and the markup stays
		// human-readable for anyone inspecting the menu icon.
		$svg  = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="black">';
		$svg .= '<path d="M1 4.5A1.5 1.5 0 0 1 2.5 3H7l2 2h8.5A1.5 1.5 0 0 1 19 6.5v9a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 1 15.5v-11z"/>';
		$svg .= '</svg>';
		$icon = 'data:image/svg+xml;utf8,' . rawurlencode( $svg );

		add_menu_page(
			'KooKoa Folders',
			'KK>_ Folders',
			'manage_options',
			'kookoa-folders',
			array( $this, 'render_settings_page' ),
			$icon,
			81
		);

		// Rename the auto-generated first submenu from "KK>_ Folder" to "Settings".
		add_submenu_page(
			'kookoa-folders',
			'KooKoa Folders Settings',
			'Settings',
			'manage_options',
			'kookoa-folders'
		);

		// Commercial menu entries live in the funnel collaborator. Registered
		// here, right after the structural Settings menu, so the submenu
		// registration order is identical to before the split.
		KookoaWPF_Funnel::instance()->register_menu_entries();
	}

	/**
	 * Redirect away from the Upgrade page when it isn't registered in the
	 * submenu tree. Delegates to the Funnel collaborator.
	 */
	public function maybe_redirect_blocked_upgrade(): void {
		KookoaWPF_Funnel::instance()->maybe_redirect_blocked_upgrade();
	}

	/**
	 * Drop the hidden "Upgrade to PRO" submenu link from the visible sidebar.
	 * Delegates to the Funnel collaborator.
	 */
	public function hide_upgrade_submenu_link(): void {
		KookoaWPF_Funnel::instance()->hide_upgrade_submenu_link();
	}

	/**
	 * Keep the top-level menu highlighted on the hidden Upgrade sub-page.
	 * Delegates to the Funnel collaborator.
	 *
	 * @param string $parent_file The current parent file slug.
	 * @return string
	 */
	public function highlight_parent_on_upgrade( $parent_file ) {
		return KookoaWPF_Funnel::instance()->highlight_parent_on_upgrade( $parent_file );
	}

	/**
	 * Render KooKoa Folders' dashboard-only admin notices.
	 * Delegates to the Funnel collaborator.
	 */
	public function render_dashboard_notices(): void {
		KookoaWPF_Funnel::instance()->render_dashboard_notices();
	}

	/**
	 * Inline CSS + JS for the top-level admin menu item styling.
	 * CSS handles font, text color and border (wp_add_inline_style = WP repo compliant).
	 * JS overrides WP's svg-painter which sets inline !important on the icon.
	 */
	public function enqueue_menu_styles(): void {
		// Pre-paint the folder icon in CSS so it never flashes svg-painter's
		// default color while menu-icon.js is still booting.
		// Both data: URLs below are fully static — no user input is interpolated,
		// so there's no dynamic value to escape. They're hardcoded at output time
		// (rather than built via str_replace) per "escape late" review guidance.
		$css = "
            #toplevel_page_kookoa-folders .wp-menu-name {
                font-family: 'Share Tech Mono', 'Courier New', monospace !important;
            }
            /* Icon: default gray */
            #adminmenu #toplevel_page_kookoa-folders .wp-menu-image {
                background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%23a7aaad'%3E%3Cpath d='M1 4.5A1.5 1.5 0 0 1 2.5 3H7l2 2h8.5A1.5 1.5 0 0 1 19 6.5v9a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 1 15.5v-11z'/%3E%3C/svg%3E\") !important;
                background-repeat: no-repeat !important;
                background-position: 7px 7px !important;
                background-size: 20px !important;
            }
            /* Hide WP svg-painter's injected <svg> so it can't flash over our CSS */
            #adminmenu #toplevel_page_kookoa-folders .wp-menu-image svg {
                display: none !important;
            }
            /* Icon: green on hover, current, or clicked */
            #adminmenu #toplevel_page_kookoa-folders:hover .wp-menu-image,
            #adminmenu #toplevel_page_kookoa-folders:focus-within .wp-menu-image,
            #adminmenu #toplevel_page_kookoa-folders.current .wp-menu-image,
            #adminmenu #toplevel_page_kookoa-folders.wp-has-current-submenu .wp-menu-image,
            #adminmenu #toplevel_page_kookoa-folders.kookoawpf-clicked .wp-menu-image {
                background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%2339FF14'%3E%3Cpath d='M1 4.5A1.5 1.5 0 0 1 2.5 3H7l2 2h8.5A1.5 1.5 0 0 1 19 6.5v9a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 1 15.5v-11z'/%3E%3C/svg%3E\") !important;
            }
            /* Clicked state — forced via JS during page navigation */
            #adminmenu #toplevel_page_kookoa-folders.kookoawpf-clicked > a {
                background: #0d1117 !important;
                border-top: 1px solid #39FF14 !important;
                border-bottom: 1px solid #39FF14 !important;
                box-shadow: inset 4px 0 0 0 #39FF14 !important;
                color: #39FF14 !important;
            }
            #adminmenu #toplevel_page_kookoa-folders.kookoawpf-clicked > a .wp-menu-name {
                color: #39FF14 !important;
            }
            /* Reserve border space to prevent layout shift on hover */
            #adminmenu #toplevel_page_kookoa-folders > a {
                border-top: 1px solid transparent !important;
                border-bottom: 1px solid transparent !important;
                transition: none !important;
            }
            /* Default state — never blue */
            #adminmenu #toplevel_page_kookoa-folders:not(.current):not(.wp-has-current-submenu) > a,
            #adminmenu #toplevel_page_kookoa-folders:not(.current):not(.wp-has-current-submenu) > a .wp-menu-name {
                color: #c9d1d9 !important;
                transition: none !important;
            }
            #adminmenu #toplevel_page_kookoa-folders:not(.current):not(.wp-has-current-submenu) > a .wp-menu-image {
                transition: none !important;
            }
            /* Active/current: text + line + background green/black */
            #adminmenu #toplevel_page_kookoa-folders.current > a .wp-menu-name,
            #adminmenu #toplevel_page_kookoa-folders.wp-has-current-submenu > a .wp-menu-name {
                color: #39FF14 !important;
            }
            #adminmenu #toplevel_page_kookoa-folders.current > a,
            #adminmenu #toplevel_page_kookoa-folders.wp-has-current-submenu > a,
            #adminmenu #toplevel_page_kookoa-folders.current > a:hover,
            #adminmenu #toplevel_page_kookoa-folders.wp-has-current-submenu > a:hover {
                background: #0d1117 !important;
                border-top: 1px solid #39FF14 !important;
                border-bottom: 1px solid #39FF14 !important;
                box-shadow: inset 4px 0 0 0 #39FF14 !important;
            }
            /* Hover / focus / active (not on current page): text + line + background green/black */
            #adminmenu #toplevel_page_kookoa-folders:not(.current):not(.wp-has-current-submenu) > a:hover .wp-menu-name,
            #adminmenu #toplevel_page_kookoa-folders:not(.current):not(.wp-has-current-submenu) > a:focus .wp-menu-name,
            #adminmenu #toplevel_page_kookoa-folders:not(.current):not(.wp-has-current-submenu) > a:active .wp-menu-name {
                color: #39FF14 !important;
            }
            #adminmenu #toplevel_page_kookoa-folders:not(.current):not(.wp-has-current-submenu) > a:hover,
            #adminmenu #toplevel_page_kookoa-folders:not(.current):not(.wp-has-current-submenu) > a:focus,
            #adminmenu #toplevel_page_kookoa-folders:not(.current):not(.wp-has-current-submenu) > a:active {
                background: #0d1117 !important;
                border-top: 1px solid #39FF14 !important;
                border-bottom: 1px solid #39FF14 !important;
                box-shadow: inset 4px 0 0 0 #39FF14 !important;
                color: #39FF14 !important;
            }
            /* Submenu hover: green neon vertical line instead of WP blue */
            #adminmenu #toplevel_page_kookoa-folders .wp-submenu a:hover,
            #adminmenu #toplevel_page_kookoa-folders .wp-submenu a:focus {
                box-shadow: inset 4px 0 0 0 #39FF14 !important;
            }
            #adminmenu #toplevel_page_kookoa-folders .wp-submenu li.current a {
                box-shadow: inset 4px 0 0 0 #39FF14 !important;
            }
        ";

		$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';

		// Load font on all admin pages so the menu item always uses Share Tech Mono.
		wp_enqueue_style( 'kookoawpf-font', KOOKOAWPF_PLUGIN_URL . 'assets/css/font' . $suffix . '.css', array(), KOOKOAWPF_VERSION );

		wp_add_inline_style( 'wp-admin', $css );

		// Menu icon painter JS — overrides WP svg-painter's inline !important on the icon.
		$icon_js_ver = $this->file_ver( KOOKOAWPF_PLUGIN_DIR . 'assets/js/menu-icon' . $suffix . '.js' );
		wp_enqueue_script(
			'kookoawpf-menu-icon',
			KOOKOAWPF_PLUGIN_URL . 'assets/js/menu-icon' . $suffix . '.js',
			array(),
			$icon_js_ver,
			true
		);
		wp_localize_script(
			'kookoawpf-menu-icon',
			'kookoawpfMenuIcon',
			array(
				'path' => 'M1 4.5A1.5 1.5 0 0 1 2.5 3H7l2 2h8.5A1.5 1.5 0 0 1 19 6.5v9a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 1 15.5v-11z',
			)
		);
	}

	/**
	 * Render the plugin settings page.
	 */
	public function render_settings_page(): void {
		if ( ! current_user_can( 'manage_options' ) ) {
			return; }
		include KOOKOAWPF_PLUGIN_DIR . 'templates/admin/settings.php';
	}

	/**
	 * Add a "Settings" link to the plugin row on the Plugins page.
	 *
	 * @param  string[] $links Existing action links.
	 * @return string[]
	 */
	public function plugin_action_links( $links ) {
		array_unshift(
			$links,
			'<a href="' . esc_url( admin_url( 'admin.php?page=kookoa-folders' ) ) . '">' . esc_html__( 'Settings', 'kookoa-folders' ) . '</a>'
		);
		return $links;
	}

	/**
	 * Append an author credit line to the admin footer on KooKoa Folders pages.
	 *
	 * Runs on `admin_footer_text` priority 20 so it appends AFTER the default
	 * WordPress "Thank you for creating with WordPress" message — never
	 * replaces it (community courtesy). Shown only on plugin-owned screens.
	 *
	 * @param string $text Footer text so far.
	 * @return string
	 */
	public function footer_credit_line( $text ) {
		if ( ! function_exists( 'get_current_screen' ) ) {
			return $text;
		}
		$screen = get_current_screen();
		if ( ! $screen ) {
			return $text;
		}
		if ( false === strpos( (string) $screen->id, 'kookoa-folders' ) ) {
			return $text;
		}
		$author_link = sprintf(
			'<a href="%s" target="_blank" rel="noopener noreferrer">%s</a>',
			esc_url( 'https://kookoa.com' ),
			esc_html( 'Adamo Lussana' )
		);
		$credit      = ' ' . sprintf(
			/* translators: %s is a link with the author's name linking to kookoa.com */
			esc_html__( '— and to %s, fueled by pizza and zero sleep.', 'kookoa-folders' ),
			$author_link
		);
		return $text . wp_kses(
			$credit,
			array(
				'a' => array(
					'href'   => array(),
					'target' => array(),
					'rel'    => array(),
				),
			)
		);
	}
}
