<?php
/**
 * Small view helpers for admin pages.
 *
 * @package Logistica
 */

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

if ( ! class_exists( 'Logistica_Tabs', false ) ) {
	require_once LOGISTICA_PLUGIN_DIR . 'includes/class-logistica-tabs.php';
}

if ( ! function_exists( 'logistica_get_nav_tabs' ) ) {
	/**
	 * Tab definitions for admin and frontend navigation.
	 *
	 * @return array<string, string>
	 */
	function logistica_get_nav_tabs() {
		return Logistica_Tabs::all_tabs();
	}
}

if ( ! function_exists( 'logistica_is_public_panel' ) ) {
	/**
	 * Panel público legacy en la app standalone (?public=1).
	 *
	 * @return bool
	 */
	function logistica_is_public_panel() {
		return ! empty( $_GET['public'] ) && empty( $_GET['config'] );
	}
}

if ( ! function_exists( 'logistica_filter_public_panel_tabs' ) ) {
	/**
	 * @param array<string, string> $tabs Tab labels keyed by slug.
	 * @return array<string, string>
	 */
	function logistica_filter_public_panel_tabs( $tabs ) {
		if ( empty( $tabs ) || ! class_exists( 'Logistica_Settings', false ) ) {
			return $tabs;
		}

		$menu_tabs = Logistica_Settings::get_frontend_menu_tabs();
		$filtered  = array();

		foreach ( $menu_tabs as $slug ) {
			if ( isset( $tabs[ $slug ] ) ) {
				$filtered[ $slug ] = $tabs[ $slug ];
			}
		}

		if ( empty( $filtered ) ) {
			return $tabs;
		}

		return Logistica_Settings::sort_tabs_by_menu_order( $filtered );
	}
}

if ( ! function_exists( 'logistica_get_nav_tabs_for_context' ) ) {
	/**
	 * Tabs visibles según contexto (admin, backend standalone o panel público).
	 *
	 * @param string $context admin|operations|backend|frontend.
	 * @return array<string, string>
	 */
	function logistica_get_nav_tabs_for_context( $context = 'admin' ) {
		if ( logistica_is_public_panel() ) {
			$tabs = Logistica_Tabs::tabs_for_context( Logistica_Tabs::CONTEXT_OPERATIONS );
			if ( class_exists( 'Logistica_Roles', false ) && is_user_logged_in() ) {
				$allowed = Logistica_Roles::get_user_tab_slugs( 'frontend' );
				if ( ! empty( $allowed ) ) {
					$role_filtered = array();
					foreach ( $allowed as $slug ) {
						if ( isset( $tabs[ $slug ] ) ) {
							$role_filtered[ $slug ] = $tabs[ $slug ];
						}
					}
					$tabs = $role_filtered;
				}
			}

			$tabs = logistica_filter_public_panel_tabs( $tabs );
			$all  = logistica_get_nav_tabs();
			if ( is_user_logged_in() && isset( $all['logistica-perfil'] ) && ! empty( $tabs ) && ! isset( $tabs['logistica-perfil'] ) ) {
				$tabs['logistica-perfil'] = $all['logistica-perfil'];
			}
			return $tabs;
		}

		if ( 'config' === $context ) {
			return Logistica_Tabs::tabs_for_context( Logistica_Tabs::CONTEXT_CONFIG );
		}

		if ( 'frontend' === $context || 'backend' === $context ) {
			return Logistica_Tabs::tabs_for_context( Logistica_Tabs::CONTEXT_OPERATIONS );
		}

		return Logistica_Tabs::tabs_for_context( $context );
	}
}

if ( ! function_exists( 'logistica_get_tab_views' ) ) {
	/**
	 * Maps tab slugs to view files.
	 *
	 * @return array<string, string>
	 */
	function logistica_get_tab_views() {
		return Logistica_Tabs::tab_views();
	}
}

if ( ! function_exists( 'logistica_view_path' ) ) {
	/**
	 * @param string $relative View file relative path.
	 * @return string
	 */
	function logistica_view_path( $relative ) {
		return Logistica_Tabs::view_path( $relative );
	}
}

if ( ! function_exists( 'logistica_get_active_tab_slug' ) ) {
	/**
	 * Pestaña activa desde la URL o, si falta, la inicial del contexto.
	 *
	 * @param string $context admin|operations|config|frontend.
	 * @return string
	 */
	function logistica_get_active_tab_slug( $context = '' ) {
		if ( isset( $_GET['tab'] ) ) {
			$tab = sanitize_key( wp_unslash( $_GET['tab'] ) );
			if ( array_key_exists( $tab, logistica_get_nav_tabs() ) ) {
				return $tab;
			}
		}

		if ( '' === $context ) {
			$context = logistica_is_config_panel() ? Logistica_Tabs::CONTEXT_CONFIG : Logistica_Tabs::CONTEXT_OPERATIONS;
		}

		return logistica_get_panel_initial_tab( $context );
	}
}

if ( ! function_exists( 'logistica_map_tab_for_context_switch' ) ) {
	/**
	 * Mapea la pestaña actual al panel destino (operaciones ↔ configuración).
	 *
	 * @param string $tab             Tab slug actual.
	 * @param string $target_context  operations|config.
	 * @return string
	 */
	function logistica_map_tab_for_context_switch( $tab, $target_context = Logistica_Tabs::CONTEXT_OPERATIONS ) {
		$tab            = sanitize_key( (string) $tab );
		$target_context = Logistica_Tabs::normalize_context( $target_context );

		if ( '' === $tab ) {
			$tab = 'logistica';
		}

		if ( Logistica_Tabs::CONTEXT_OPERATIONS === $target_context && Logistica_Tabs::is_admin_only_tab( $tab ) ) {
			$tab = 'logistica';
		}

		$visible = array_keys( Logistica_Tabs::tabs_for_context( $target_context ) );
		if ( in_array( $tab, $visible, true ) ) {
			return $tab;
		}

		if ( in_array( 'logistica', $visible, true ) ) {
			return 'logistica';
		}

		return (string) ( $visible[0] ?? 'logistica' );
	}
}

if ( ! function_exists( 'logistica_get_panel_initial_tab' ) ) {
	/**
	 * @param string $context admin|operations|backend|frontend.
	 * @return string
	 */
	function logistica_get_panel_initial_tab( $context = 'operations' ) {
		if ( 'config' === $context ) {
			return Logistica_Tabs::initial_tab( Logistica_Tabs::CONTEXT_CONFIG );
		}

		if ( 'admin' === $context ) {
			return Logistica_Tabs::initial_tab( Logistica_Tabs::CONTEXT_ADMIN );
		}

		return Logistica_Tabs::initial_tab( Logistica_Tabs::CONTEXT_OPERATIONS );
	}
}

if ( ! function_exists( 'logistica_is_config_panel' ) ) {
	/**
	 * Panel de configuración en jb-planeacion-backend (?config=1).
	 *
	 * @return bool
	 */
	function logistica_is_config_panel() {
		if ( empty( $_GET['config'] ) ) {
			return false;
		}

		if ( ! class_exists( 'Logistica_Roles', false ) || ! Logistica_Roles::user_can_manage_config() ) {
			return false;
		}

		return true;
	}
}

if ( ! function_exists( 'logistica_is_etiqueta_settings_view' ) ) {
	/**
	 * Vista de configuración de etiquetas (wp-admin o panel ?config=1).
	 *
	 * @return bool
	 */
	function logistica_is_etiqueta_settings_view() {
		return is_admin() || logistica_is_config_panel();
	}
}

if ( ! function_exists( 'logistica_can_edit_etiqueta_settings' ) ) {
	/**
	 * Permiso real para guardar tamaño / numeración manual de etiquetas.
	 * Solo capacidad: no depender de is_admin()/$_GET['config'] (falla en AJAX).
	 *
	 * @return bool
	 */
	function logistica_can_edit_etiqueta_settings() {
		if ( class_exists( 'Logistica_Roles', false ) && Logistica_Roles::user_can_manage_config() ) {
			return true;
		}

		return current_user_can( 'manage_options' );
	}
}

if ( ! function_exists( 'logistica_can_show_etiqueta_settings_editor' ) ) {
	/**
	 * UI editable de tamaño / numeración: solo en wp-admin o panel ?config=1.
	 * En operaciones se muestra solo lectura.
	 *
	 * @return bool
	 */
	function logistica_can_show_etiqueta_settings_editor() {
		return logistica_is_etiqueta_settings_view() && logistica_can_edit_etiqueta_settings();
	}
}

if ( ! function_exists( 'logistica_is_rotulo_rollos_settings_view' ) ) {
	/**
	 * Vista de configuración de rótulos rollos (wp-admin o panel ?config=1).
	 *
	 * @return bool
	 */
	function logistica_is_rotulo_rollos_settings_view() {
		return is_admin() || logistica_is_config_panel();
	}
}

if ( ! function_exists( 'logistica_can_edit_rotulo_rollos_settings' ) ) {
	/**
	 * Permiso real para editar/guardar tamaño de rótulo.
	 * No depender solo del contexto de vista: en post.php standalone is_admin() es false.
	 *
	 * @return bool
	 */
	function logistica_can_edit_rotulo_rollos_settings() {
		if ( class_exists( 'Logistica_Roles', false ) && Logistica_Roles::user_can_manage_config() ) {
			return true;
		}

		return logistica_is_rotulo_rollos_settings_view();
	}
}

if ( ! function_exists( 'logistica_can_manage_etiqueta_catalog_options' ) ) {
	/**
	 * Gestión de listas Tipo producto / Mercado / Tamaño.
	 *
	 * @return bool
	 */
	function logistica_can_manage_etiqueta_catalog_options() {
		if ( ! class_exists( 'Logistica_Roles', false ) ) {
			return false;
		}

		if ( Logistica_Roles::user_can_tab( 'logistica-codigos-etiqueta', 'backend' ) ) {
			return true;
		}

		return logistica_is_config_panel() && Logistica_Roles::user_can_manage_config();
	}
}

if ( ! function_exists( 'logistica_config_panel_url' ) ) {
	/**
	 * @param string $tab Tab slug.
	 * @return string
	 */
	function logistica_config_panel_url( $tab = '' ) {
		$url = class_exists( 'Logistica_Roles', false )
			? Logistica_Roles::get_operations_panel_url( '' )
			: home_url( '/' );

		$url = add_query_arg( 'config', '1', $url );

		$tab = sanitize_key( (string) $tab );
		if ( '' !== $tab && array_key_exists( $tab, logistica_get_nav_tabs() ) ) {
			$url = add_query_arg( 'tab', $tab, $url );
		}

		return $url;
	}
}

if ( ! function_exists( 'logistica_get_config_section_ids' ) ) {
	/**
	 * IDs de ancla de la pestaña Configuración general.
	 *
	 * @return list<string>
	 */
	function logistica_get_config_section_ids() {
		$sections = array(
			'logistica-config-menu-order',
			'logistica-config-panel-publico',
			'logistica-config-roles',
			'logistica-config-usuarios',
			'logistica-config-sistema',
			'logistica-config-trazabilidad',
		);

		if ( ! defined( 'LOGISTICA_STANDALONE' ) || ! LOGISTICA_STANDALONE ) {
			array_unshift( $sections, 'logistica-config-operaciones' );
		}

		return $sections;
	}
}

if ( ! function_exists( 'logistica_sanitize_config_section' ) ) {
	/**
	 * @param string $section Section anchor id.
	 * @return string
	 */
	function logistica_sanitize_config_section( $section ) {
		$section = sanitize_key( (string) $section );

		if ( in_array( $section, logistica_get_config_section_ids(), true ) ) {
			return $section;
		}

		return 'logistica-config-usuarios';
	}
}

if ( ! function_exists( 'logistica_config_return_section' ) ) {
	/**
	 * Sección de configuración desde la que se abrió un formulario auxiliar.
	 *
	 * @return string
	 */
	function logistica_config_return_section() {
		if ( ! empty( $_REQUEST['return_section'] ) ) {
			return logistica_sanitize_config_section( wp_unslash( (string) $_REQUEST['return_section'] ) );
		}

		return 'logistica-config-usuarios';
	}
}

if ( ! function_exists( 'logistica_config_section_url' ) ) {
	/**
	 * URL de configuración apuntando a una sección concreta.
	 *
	 * @param string $section_id Anchor id.
	 * @return string
	 */
	function logistica_config_section_url( $section_id = 'logistica-config-usuarios' ) {
		$section_id = logistica_sanitize_config_section( $section_id );

		return logistica_config_panel_url( 'logistica-configuracion' ) . '#' . $section_id;
	}
}

if ( ! function_exists( 'logistica_new_user_url' ) ) {
	/**
	 * URL para crear un usuario en la app standalone.
	 *
	 * @param string $return_section Sección de configuración de retorno.
	 * @return string
	 */
	function logistica_new_user_url( $return_section = 'logistica-config-usuarios' ) {
		return add_query_arg(
			'return_section',
			logistica_sanitize_config_section( $return_section ),
			home_url( 'user-new.php' )
		);
	}
}

if ( ! function_exists( 'logistica_operations_panel_url' ) ) {
	/**
	 * URL del panel operativo (sin modo configuración).
	 *
	 * @param string $tab Tab slug.
	 * @return string
	 */
	function logistica_operations_panel_url( $tab = '' ) {
		$url = class_exists( 'Logistica_Roles', false )
			? Logistica_Roles::get_operations_panel_url( $tab )
			: home_url( '/' );

		return remove_query_arg( 'config', $url );
	}
}

if ( ! function_exists( 'logistica_configuracion_tab_url' ) ) {
	/**
	 * URL de la pestaña Configuración según superficie (wp-admin o backend ?config=1).
	 *
	 * @return string
	 */
	function logistica_configuracion_tab_url() {
		if ( function_exists( 'logistica_is_config_panel' ) && logistica_is_config_panel() ) {
			return logistica_config_panel_url( 'logistica-configuracion' );
		}

		if ( is_admin() ) {
			return logistica_tab_url( 'logistica-configuracion', 'admin' );
		}

		return logistica_config_panel_url( 'logistica-configuracion' );
	}
}

if ( ! function_exists( 'logistica_get_current_tab' ) ) {
	/**
	 * Resolves the active tab from query arguments.
	 *
	 * @return string
	 */
	function logistica_get_current_tab() {
		if ( isset( $_GET['tab'] ) ) {
			$tab = sanitize_key( wp_unslash( $_GET['tab'] ) );
			if ( array_key_exists( $tab, logistica_get_nav_tabs() ) ) {
				return $tab;
			}
		}

		$page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : 'logistica';
		if ( 'logistica' !== $page && array_key_exists( $page, logistica_get_nav_tabs() ) ) {
			return $page;
		}

		return 'logistica-configuracion';
	}
}

if ( ! function_exists( 'logistica_get_frontend_initial_tab' ) ) {
	/**
	 * Pestaña activa del panel público según la URL o la primera visible.
	 *
	 * @return string
	 */
	function logistica_get_frontend_initial_tab() {
		return logistica_get_panel_initial_tab( 'operations' );
	}
}

if ( ! function_exists( 'logistica_tab_url' ) ) {
	/**
	 * Builds a tab URL for admin or frontend.
	 *
	 * @param string $tab     Tab slug.
	 * @param string $context admin|frontend.
	 * @return string
	 */
	function logistica_tab_url( $tab, $context = 'admin' ) {
		if ( 'config' === $context ) {
			return Logistica_Tabs::tab_url( $tab, Logistica_Tabs::CONTEXT_CONFIG );
		}

		if ( 'frontend' === $context || 'backend' === $context || 'operations' === $context ) {
			return Logistica_Tabs::tab_url( $tab, Logistica_Tabs::CONTEXT_OPERATIONS );
		}

		return Logistica_Tabs::tab_url( $tab, Logistica_Tabs::CONTEXT_ADMIN );
	}
}

if ( ! function_exists( 'logistica_redirect_legacy_cierre_caracteristicas_section' ) ) {
	/**
	 * Redirige URLs antiguas ?cierre_section=caracteristicas a la pestaña principal.
	 *
	 * @return void
	 */
	function logistica_redirect_legacy_cierre_caracteristicas_section() {
		if ( ! isset( $_GET['cierre_section'] ) || 'caracteristicas' !== sanitize_key( wp_unslash( $_GET['cierre_section'] ) ) ) {
			return;
		}

		$tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : '';

		if ( is_admin() ) {
			$page = isset( $_GET['page'] ) ? sanitize_key( wp_unslash( $_GET['page'] ) ) : 'logistica';
			if ( 'logistica' !== $page ) {
				return;
			}
			if ( '' !== $tab && 'logistica-cierre-ordenes' !== $tab ) {
				return;
			}
			$context = 'admin';
		} else {
			if ( '' !== $tab && 'logistica-cierre-ordenes' !== $tab ) {
				return;
			}
			$post = get_post();
			$context = ( $post instanceof WP_Post && logistica_is_panel_post( $post ) ) ? 'operations' : 'admin';
		}

		$redirect = logistica_tab_url( 'logistica-cierre-caracteristicas', $context );

		if ( isset( $_GET['edit_id'] ) ) {
			$redirect = add_query_arg( 'edit_id', (int) wp_unslash( $_GET['edit_id'] ), $redirect );
		}

		if ( isset( $_GET['logistica_status'] ) ) {
			$redirect = add_query_arg( 'logistica_status', sanitize_key( wp_unslash( $_GET['logistica_status'] ) ), $redirect );
		}

		wp_safe_redirect( $redirect );
		exit;
	}
}

if ( ! function_exists( 'logistica_is_frontend_panel_post' ) ) {
	/**
	 * Whether a post renders the public inventory panel.
	 *
	 * @param WP_Post|null $post Post object.
	 * @return bool
	 */
	function logistica_is_frontend_panel_post( $post ) {
		if ( defined( 'LOGISTICA_STANDALONE' ) && LOGISTICA_STANDALONE ) {
			return is_object( $post ) && ! empty( $post->ID );
		}

		if ( ! $post instanceof WP_Post ) {
			return false;
		}

		$page_id = class_exists( 'Logistica_Settings', false )
			? Logistica_Settings::get_frontend_page_id()
			: (int) get_option( 'logistica_frontend_page_id', 0 );

		if ( $page_id && (int) $post->ID === $page_id ) {
			return true;
		}

		return has_shortcode( $post->post_content, 'logistica_panel' )
			|| has_shortcode( $post->post_content, 'latingraf_inventarios' );
	}
}

if ( ! function_exists( 'logistica_is_backend_panel_post' ) ) {
	/**
	 * Whether a post renders the standalone backend panel.
	 *
	 * @param WP_Post|null $post Post object.
	 * @return bool
	 */
	function logistica_is_backend_panel_post( $post ) {
		if ( defined( 'LOGISTICA_STANDALONE' ) && LOGISTICA_STANDALONE ) {
			return is_object( $post ) && ! empty( $post->ID );
		}

		if ( ! $post instanceof WP_Post ) {
			return false;
		}

		$page_id = class_exists( 'Logistica_Roles', false )
			? Logistica_Roles::get_backend_page_id()
			: (int) get_option( 'logistica_backend_page_id', 0 );

		if ( $page_id && (int) $post->ID === $page_id ) {
			return true;
		}

		return has_shortcode( $post->post_content, 'logistica_backend' );
	}
}

if ( ! function_exists( 'logistica_is_panel_post' ) ) {
	/**
	 * Whether a post renders any JB Planeación panel (frontend or backend).
	 *
	 * @param WP_Post|null $post Post object.
	 * @return bool
	 */
	function logistica_is_panel_post( $post ) {
		return logistica_is_frontend_panel_post( $post ) || logistica_is_backend_panel_post( $post );
	}
}

if ( ! function_exists( 'logistica_get_panel_context_for_post' ) ) {
	/**
	 * @param WP_Post|null $post Post object.
	 * @return string frontend|backend
	 */
	function logistica_get_panel_context_for_post( $post ) {
		if ( logistica_is_backend_panel_post( $post ) || logistica_is_frontend_panel_post( $post ) ) {
			return Logistica_Tabs::CONTEXT_OPERATIONS;
		}

		return Logistica_Tabs::CONTEXT_OPERATIONS;
	}
}

if ( ! function_exists( 'logistica_get_backend_initial_tab' ) ) {
	/**
	 * Pestaña activa del panel backend según URL o primera visible.
	 *
	 * @return string
	 */
	function logistica_get_backend_initial_tab() {
		return logistica_get_panel_initial_tab( 'operations' );
	}
}

if ( ! function_exists( 'logistica_get_frontend_panel_url' ) ) {
	/**
	 * URL de la pagina publica del panel (shortcode inventarios).
	 *
	 * @param string $tab Pestaña opcional (mismo slug que en admin).
	 * @return string
	 */
	function logistica_get_frontend_panel_url( $tab = '' ) {
		if ( defined( 'LOGISTICA_STANDALONE' ) && LOGISTICA_STANDALONE ) {
			$url = add_query_arg( 'public', '1', home_url( '/' ) );
			$tab = sanitize_key( (string) $tab );
			if ( '' !== $tab ) {
				$url = add_query_arg( 'tab', $tab, $url );
			}
			return $url;
		}

		if ( class_exists( 'Logistica_Settings', false ) ) {
			$page_id = Logistica_Settings::get_frontend_page_id();
			if ( $page_id ) {
				$url = get_permalink( $page_id );
				$tab = sanitize_key( (string) $tab );
				if ( '' !== $tab ) {
					$url = add_query_arg( 'tab', $tab, $url );
				}
				return $url;
			}
		}

		if ( class_exists( 'Logistica_Roles', false ) ) {
			return Logistica_Roles::get_operations_panel_url( $tab );
		}

		return home_url( '/' );
	}
}

if ( ! function_exists( 'logistica_admin_frontend_link' ) ) {
	/**
	 * Boton para abrir la misma seccion en el panel publico.
	 *
	 * @param string $tab Slug de pestaña; vacio = pestaña actual.
	 * @return void
	 */
	function logistica_admin_frontend_link( $tab = '' ) {
		if ( ! is_admin() || ! ( current_user_can( 'manage_options' ) || ( class_exists( 'Logistica_Roles', false ) && Logistica_Roles::user_can_access_plugin() ) ) ) {
			return;
		}

		if ( '' === $tab ) {
			$tab = logistica_get_current_tab();
		}

		$frontend_tabs = logistica_get_nav_tabs_for_context( 'frontend' );
		if ( ! isset( $frontend_tabs[ $tab ] ) ) {
			$tab = (string) array_key_first( $frontend_tabs );
		}

		$url = logistica_get_frontend_panel_url( $tab );
		?>
		<a
			class="button logistica-frontend-link"
			href="<?php echo esc_url( $url ); ?>"
			target="_blank"
			rel="noopener noreferrer"
		>
			<span class="logistica-frontend-link__icon dashicons dashicons-external" aria-hidden="true"></span>
			<?php esc_html_e( 'Ver en el sitio', 'logistica' ); ?>
		</a>
		<?php
	}
}

if ( ! function_exists( 'logistica_is_tabbed_shell' ) ) {
	/**
	 * Whether views are rendered inside the frontend tab shell.
	 *
	 * @return bool
	 */
	function logistica_is_tabbed_shell() {
		return ! empty( $GLOBALS['logistica_tabbed_shell'] );
	}
}

if ( ! function_exists( 'logistica_render_status_notices' ) ) {
	/**
	 * Prints save/import notices from the query string.
	 *
	 * @return void
	 */
	function logistica_render_status_notices() {
		if ( isset( $_GET['logistica_status'] ) && 'saved' === $_GET['logistica_status'] ) {
			logistica_notice( __( 'Registro guardado correctamente.', 'logistica' ) );
		}
		if ( isset( $_GET['logistica_status'] ) && 'error' === $_GET['logistica_status'] ) {
			logistica_notice( __( 'No se pudo guardar la configuración. Recarga la página e intenta de nuevo.', 'logistica' ), 'warning' );
		}
		if ( isset( $_GET['logistica_status'] ) && 'imported' === $_GET['logistica_status'] ) {
			$import_key = 'logistica-inventario-bodega' === logistica_get_current_tab()
				? 'logistica_warehouse_import_'
				: ( 'logistica-cierre-caracteristicas' === logistica_get_current_tab()
					? 'logistica_cierre_caracteristicas_import_'
					: ( 'logistica-codigos-etiqueta' === logistica_get_current_tab()
						? 'logistica_etiqueta_productos_import_'
						: ( 'logistica-revision-minuta' === logistica_get_current_tab()
							? 'logistica_minutas_import_'
							: 'logistica_cierre_import_' ) ) );
			$import_result = get_transient( $import_key . get_current_user_id() );
			delete_transient( $import_key . get_current_user_id() );
			$import_message = is_array( $import_result ) && ! empty( $import_result['message'] )
				? $import_result['message']
				: __( 'Datos importados desde Excel correctamente.', 'logistica' );
			logistica_notice( $import_message );
		}
		if ( isset( $_GET['logistica_status'] ) && 'import_error' === $_GET['logistica_status'] ) {
			$import_key = 'logistica-inventario-bodega' === logistica_get_current_tab()
				? 'logistica_warehouse_import_'
				: ( 'logistica-cierre-caracteristicas' === logistica_get_current_tab()
					? 'logistica_cierre_caracteristicas_import_'
					: ( 'logistica-codigos-etiqueta' === logistica_get_current_tab()
						? 'logistica_etiqueta_productos_import_'
						: ( 'logistica-revision-minuta' === logistica_get_current_tab()
							? 'logistica_minutas_import_'
							: 'logistica_cierre_import_' ) ) );
			$import_result = get_transient( $import_key . get_current_user_id() );
			delete_transient( $import_key . get_current_user_id() );
			$import_message = is_array( $import_result ) && ! empty( $import_result['message'] )
				? $import_result['message']
				: __( 'No se pudo importar el archivo Excel.', 'logistica' );
			logistica_notice( $import_message, 'warning' );
		}
		if ( isset( $_GET['logistica_status'] ) && 'menu_saved' === $_GET['logistica_status'] ) {
			logistica_notice( __( 'Menú del panel público actualizado.', 'logistica' ) );
		}
		if ( isset( $_GET['logistica_status'] ) && 'roles_saved' === $_GET['logistica_status'] ) {
			logistica_notice( __( 'Roles y permisos actualizados.', 'logistica' ) );
		}
		if ( isset( $_GET['logistica_status'] ) && 'caracteristica_saved' === $_GET['logistica_status'] ) {
			logistica_notice( __( 'Característica guardada correctamente.', 'logistica' ) );
		}
		if ( isset( $_GET['logistica_status'] ) && 'caracteristica_deleted' === $_GET['logistica_status'] ) {
			logistica_notice( __( 'Característica eliminada correctamente.', 'logistica' ) );
		}
		if ( isset( $_GET['logistica_status'] ) && 'caracteristica_error' === $_GET['logistica_status'] ) {
			$error_message = get_transient( 'logistica_cierre_caracteristica_error_' . get_current_user_id() );
			delete_transient( 'logistica_cierre_caracteristica_error_' . get_current_user_id() );
			logistica_notice(
				is_string( $error_message ) && '' !== $error_message
					? $error_message
					: __( 'No se pudo guardar la característica.', 'logistica' ),
				'warning'
			);
		}
	}
}

if ( ! function_exists( 'logistica_get_panel_heading_sections' ) ) {
	/**
	 * Textos del encabezado de panel por superficie.
	 *
	 * @return array<string, array{brand: string, title: string, subtitle: string, modifier: string}>
	 */
	function logistica_get_panel_heading_sections() {
		return array(
			'operations' => array(
				'brand'    => __( 'JB Planeación', 'logistica' ),
				'title'    => __( 'Operaciones', 'logistica' ),
				'subtitle' => __( 'Panel operativo del equipo', 'logistica' ),
				'modifier' => 'operations',
			),
			'config'     => array(
				'brand'    => __( 'JB Planeación', 'logistica' ),
				'title'    => __( 'Configuración', 'logistica' ),
				'subtitle' => __( 'Roles, usuarios y ajustes del sistema', 'logistica' ),
				'modifier' => 'config',
			),
			'default'    => array(
				'brand'    => __( 'JB Planeación', 'logistica' ),
				'title'    => __( 'Panel', 'logistica' ),
				'subtitle' => '',
				'modifier' => 'default',
			),
		);
	}
}

if ( ! function_exists( 'logistica_render_panel_heading' ) ) {
	/**
	 * Encabezado de marca + sección para operaciones y configuración.
	 *
	 * @param string $section operations|config|default.
	 * @return void
	 */
	function logistica_render_panel_heading( $section = 'default' ) {
		$sections = logistica_get_panel_heading_sections();
		$section  = sanitize_key( (string) $section );
		$data     = $sections[ $section ] ?? $sections['default'];
		?>
		<div class="logistica-header__title-block logistica-header__title-block--<?php echo esc_attr( $data['modifier'] ); ?>">
			<p class="logistica-header__brand"><?php echo esc_html( $data['brand'] ); ?></p>
			<h1 class="logistica-header__section"><?php echo esc_html( $data['title'] ); ?></h1>
			<?php if ( ! empty( $data['subtitle'] ) ) : ?>
				<p class="logistica-header__subtitle"><?php echo esc_html( $data['subtitle'] ); ?></p>
			<?php endif; ?>
		</div>
		<?php
	}
}

if ( ! function_exists( 'logistica_render_panel_user_bar' ) ) {
	/**
	 * Barra de usuario del header.
	 * En operaciones: enlace a Mi perfil (oculto en Inicio y en la propia página de perfil).
	 * En configuración: cerrar sesión.
	 *
	 * @param array $args {
	 *     @type bool $hidden Ocultar la barra en el primer render.
	 * }
	 * @return void
	 */
	function logistica_render_panel_user_bar( $args = array() ) {
		if ( ! is_user_logged_in() ) {
			return;
		}

		$args       = is_array( $args ) ? $args : array();
		$user       = wp_get_current_user();
		$role_label = class_exists( 'Logistica_Roles', false ) ? Logistica_Roles::get_user_role_label() : '';
		$is_config  = function_exists( 'logistica_is_config_panel' ) && logistica_is_config_panel();
		$bar_hidden = ! empty( $args['hidden'] );
		?>
		<div class="logistica-header__user-bar" data-logistica-header-user-bar<?php echo $bar_hidden ? ' hidden aria-hidden="true"' : ''; ?>>
			<div class="logistica-header__user">
				<span class="logistica-header__user-name"><?php echo esc_html( $user->display_name ); ?></span>
				<?php if ( '' !== $role_label ) : ?>
					<span class="logistica-header__user-role"><?php echo esc_html( $role_label ); ?></span>
				<?php endif; ?>
			</div>
			<?php if ( $is_config ) : ?>
				<button type="button" class="logistica-header__logout" data-logistica-panel-logout>
					<span class="dashicons dashicons-migrate" aria-hidden="true"></span>
					<?php esc_html_e( 'Cerrar sesión', 'logistica' ); ?>
				</button>
			<?php else : ?>
				<button
					type="button"
					class="logistica-header__profile"
					data-logistica-open-profile
					data-profile-tab="logistica-perfil"
					data-logistica-profile-link
				>
					<span class="dashicons dashicons-admin-users" aria-hidden="true"></span>
					<?php esc_html_e( 'Ver perfil', 'logistica' ); ?>
				</button>
			<?php endif; ?>
		</div>
		<?php
	}
}

if ( ! function_exists( 'logistica_app_rail' ) ) {
	/**
	 * Barra fija lateral con iconos (acceso config/operaciones, menú y navegación de sección).
	 *
	 * @param string $context admin|operations|config.
	 * @return void
	 */
	function logistica_app_rail( $context = '' ) {
		if ( '' === $context ) {
			$context = logistica_is_config_panel() ? Logistica_Tabs::CONTEXT_CONFIG : Logistica_Tabs::CONTEXT_OPERATIONS;
		}

		$context = Logistica_Tabs::normalize_context( $context );

		$current_tab = logistica_get_active_tab_slug( $context );

		$can_manage_config = class_exists( 'Logistica_Roles', false ) && Logistica_Roles::user_can_manage_config();
		$show_config_pin   = $can_manage_config && Logistica_Tabs::CONTEXT_CONFIG !== $context;
		$show_operations_pin = Logistica_Tabs::CONTEXT_CONFIG === $context;

		if ( $show_config_pin && Logistica_Tabs::CONTEXT_ADMIN === $context ) {
			$config_pin_url = logistica_configuracion_tab_url();
		} elseif ( $show_config_pin ) {
			$config_pin_url = logistica_config_panel_url(
				logistica_map_tab_for_context_switch( $current_tab, Logistica_Tabs::CONTEXT_CONFIG )
			);
		} else {
			$config_pin_url = '';
		}

		if ( $show_operations_pin ) {
			$operations_pin_url = logistica_operations_panel_url(
				logistica_map_tab_for_context_switch( $current_tab, Logistica_Tabs::CONTEXT_OPERATIONS )
			);
		} else {
			$operations_pin_url = '';
		}

		$show_rail_nav_controls = true;
		$home_url               = logistica_operations_panel_url( 'logistica' );
		$is_home_active         = Logistica_Tabs::CONTEXT_OPERATIONS === $context && 'logistica' === $current_tab;
		$show_context_pin       = ( $show_operations_pin && '' !== $operations_pin_url ) || ( $show_config_pin && '' !== $config_pin_url );
		?>
		<aside class="logistica-app-rail" data-logistica-app-rail aria-label="<?php esc_attr_e( 'Accesos rápidos', 'logistica' ); ?>">
			<a
				class="logistica-app-rail__pin logistica-app-rail__pin--home<?php echo $is_home_active ? ' is-active' : ''; ?>"
				href="<?php echo esc_url( $home_url ); ?>"
				title="<?php esc_attr_e( 'Inicio', 'logistica' ); ?>"
			>
				<span class="dashicons dashicons-admin-home" aria-hidden="true"></span>
				<span class="screen-reader-text"><?php esc_html_e( 'Inicio', 'logistica' ); ?></span>
			</a>
			<?php if ( $show_rail_nav_controls ) : ?>
			<button
				type="button"
				class="logistica-app-rail__btn"
				data-action="toggle-inventory-nav"
				aria-expanded="false"
				aria-controls="logistica-inventory-nav-list"
				title="<?php esc_attr_e( 'Menú', 'logistica' ); ?>"
			>
				<span class="dashicons dashicons-menu" aria-hidden="true"></span>
				<span class="screen-reader-text"><?php esc_html_e( 'Menú', 'logistica' ); ?></span>
			</button>
			<button
				type="button"
				class="logistica-app-rail__btn logistica-app-rail__btn--section"
				data-action="toggle-section-nav"
				aria-expanded="false"
				title="<?php esc_attr_e( 'Navegación', 'logistica' ); ?>"
				hidden
			>
				<span class="logistica-app-rail__icon logistica-app-rail__icon--section-nav" aria-hidden="true"></span>
				<span class="screen-reader-text"><?php esc_html_e( 'Navegación', 'logistica' ); ?></span>
			</button>
			<?php endif; ?>
			<?php if ( $show_context_pin ) : ?>
			<div class="logistica-app-rail__footer">
				<?php if ( $show_operations_pin && '' !== $operations_pin_url ) : ?>
					<a
						class="logistica-app-rail__pin logistica-app-rail__pin--operations"
						data-logistica-context-pin="operations"
						href="<?php echo esc_url( $operations_pin_url ); ?>"
						title="<?php esc_attr_e( 'Volver a operaciones', 'logistica' ); ?>"
					>
						<span class="dashicons dashicons-arrow-left-alt" aria-hidden="true"></span>
						<span class="screen-reader-text"><?php esc_html_e( 'Volver a operaciones', 'logistica' ); ?></span>
					</a>
				<?php elseif ( $show_config_pin && '' !== $config_pin_url ) : ?>
					<a
						class="logistica-app-rail__pin logistica-app-rail__pin--config"
						data-logistica-context-pin="config"
						href="<?php echo esc_url( $config_pin_url ); ?>"
						title="<?php esc_attr_e( 'Configuración', 'logistica' ); ?>"
					>
						<span class="dashicons dashicons-admin-generic" aria-hidden="true"></span>
						<span class="screen-reader-text"><?php esc_html_e( 'Configuración', 'logistica' ); ?></span>
					</a>
				<?php endif; ?>
			</div>
			<?php endif; ?>
		</aside>
		<?php if ( $show_rail_nav_controls ) : ?>
		<button
			type="button"
			class="logistica-section-nav-backdrop"
			data-action="toggle-section-nav"
			aria-label="<?php esc_attr_e( 'Cerrar navegación', 'logistica' ); ?>"
			aria-hidden="true"
			tabindex="-1"
		></button>
		<?php endif; ?>
		<?php
	}
}

if ( ! function_exists( 'logistica_nav_tabs' ) ) {
	/**
	 * WordPress-style horizontal tabs (nav-tab-wrapper).
	 *
	 * @param string $context admin|frontend.
	 * @return void
	 */
	function logistica_nav_tabs( $context = 'admin' ) {
		$context = Logistica_Tabs::normalize_context( 'admin' === $context ? 'admin' : $context );

		if ( Logistica_Tabs::CONTEXT_CONFIG === $context || Logistica_Tabs::CONTEXT_OPERATIONS === $context ) {
			$current = logistica_get_active_tab_slug( $context );
		} else {
			$current = logistica_get_current_tab();
		}

		$tabs = logistica_get_nav_tabs_for_context( $context );
		logistica_app_rail( $context );

		$nav_modifier = Logistica_Tabs::CONTEXT_CONFIG === $context
			? ' logistica-inventory-nav--config'
			: ( Logistica_Tabs::CONTEXT_OPERATIONS === $context ? ' logistica-inventory-nav--operations' : ' logistica-inventory-nav--admin' );
		?>
		<aside
			class="logistica-nav-tab-wrap logistica-inventory-nav<?php echo esc_attr( $nav_modifier ); ?>"
			aria-label="<?php esc_attr_e( 'Secciones de inventario', 'logistica' ); ?>"
			data-logistica-inventory-nav
		>
			<div class="logistica-inventory-nav__head">
				<button
					type="button"
					class="logistica-inventory-nav__toggle"
					data-action="toggle-inventory-nav"
					aria-expanded="false"
					aria-controls="logistica-inventory-nav-list"
					title="<?php esc_attr_e( 'Ocultar menú', 'logistica' ); ?>"
				>&minus;</button>
			</div>
			<div class="logistica-inventory-nav__inner">
				<div
					id="logistica-inventory-nav-list"
					class="nav-tab-wrapper wp-clearfix logistica-nav-tab-wrapper logistica-inventory-nav__list"
					data-logistica-tabs="<?php echo esc_attr( $context ); ?>"
				>
					<?php
					if ( Logistica_Tabs::CONTEXT_ADMIN === $context && current_user_can( 'manage_options' ) ) :
						$operations_tabs    = logistica_get_nav_tabs_for_context( 'operations' );
						$operations_nav_tab = isset( $operations_tabs[ $current ] ) ? $current : (string) array_key_first( $operations_tabs );
						if ( $operations_nav_tab ) :
							?>
						<a
							class="logistica-inventory-nav__frontend-link"
							href="<?php echo esc_url( logistica_get_frontend_panel_url( $operations_nav_tab ) ); ?>"
							target="_blank"
							rel="noopener noreferrer"
						>
							<span class="dashicons dashicons-external" aria-hidden="true"></span>
							<?php esc_html_e( 'Panel operativo', 'logistica' ); ?>
						</a>
							<?php
						endif;
					endif;
					?>
					<?php foreach ( $tabs as $slug => $label ) : ?>
						<?php
						$is_hidden_nav = Logistica_Tabs::is_hidden_nav_tab( $slug );
						$link_classes  = 'nav-tab logistica-inventory-nav__link';
						if ( $slug === $current ) {
							$link_classes .= ' nav-tab-active';
						}
						if ( $is_hidden_nav ) {
							$link_classes .= ' logistica-inventory-nav__link--hidden';
						}
						?>
						<a
							id="<?php echo esc_attr( 'logistica-tab-link-' . $slug ); ?>"
							class="<?php echo esc_attr( $link_classes ); ?>"
							href="<?php echo esc_url( logistica_tab_url( $slug, $context ) ); ?>"
							data-logistica-tab="<?php echo esc_attr( $slug ); ?>"
							<?php echo $is_hidden_nav ? ' hidden aria-hidden="true" tabindex="-1"' : ''; ?>
							<?php echo $slug === $current ? ' aria-current="page"' : ''; ?>
						><?php echo esc_html( $label ); ?></a>
					<?php endforeach; ?>
				</div>
			</div>
		</aside>
		<button
			type="button"
			class="logistica-inventory-nav-backdrop"
			data-action="toggle-inventory-nav"
			aria-label="<?php esc_attr_e( 'Cerrar menú', 'logistica' ); ?>"
			tabindex="-1"
		></button>
		<?php
	}
}

if ( ! function_exists( 'logistica_admin_tab_bar' ) ) {
	/**
	 * Barra horizontal de pestañas siempre visible en el backend.
	 *
	 * @return void
	 */
	function logistica_admin_tab_bar() {
		if ( logistica_is_tabbed_shell() ) {
			return;
		}

		$current = logistica_get_current_tab();
		$tabs    = logistica_get_nav_tabs_for_context( 'admin' );
		?>
		<div class="logistica-front-nav-wrap logistica-admin-tab-bar-wrap">
			<nav
				class="logistica-front-nav logistica-admin-tab-bar"
				aria-label="<?php esc_attr_e( 'Secciones del plugin', 'logistica' ); ?>"
			>
				<?php foreach ( $tabs as $slug => $label ) : ?>
					<a
						id="<?php echo esc_attr( 'logistica-admin-tab-' . $slug ); ?>"
						href="<?php echo esc_url( logistica_tab_url( $slug, 'admin' ) ); ?>"
						class="<?php echo $slug === $current ? 'is-active' : ''; ?>"
						data-logistica-tab="<?php echo esc_attr( $slug ); ?>"
						<?php echo $slug === $current ? ' aria-current="page"' : ''; ?>
					><?php echo esc_html( $label ); ?></a>
				<?php endforeach; ?>
			</nav>
		</div>
		<?php
	}
}

if ( ! function_exists( 'logistica_admin_header' ) ) {
	/**
	 * Prints the shared page header.
	 *
	 * @param string $title Page title.
	 * @return void
	 */
	function logistica_admin_header( $title ) {
		if ( logistica_is_tabbed_shell() ) {
			return;
		}
		?>
		<div class="wrap logistica-admin is-nav-collapsed" data-logistica-inventory-shell>
			<?php logistica_nav_tabs( 'admin' ); ?>
			<div class="logistica-inventory-body">
			<header class="logistica-header">
				<div class="logistica-header__intro">
					<h1><?php echo esc_html( $title ); ?></h1>
					<p><?php esc_html_e( 'Inventario bodega, calculadoras y cierre de órdenes.', 'logistica' ); ?></p>
				</div>
				<div class="logistica-header__actions">
					<?php logistica_admin_frontend_link(); ?>
				</div>
			</header>
			<?php logistica_admin_tab_bar(); ?>
			<?php
			logistica_render_status_notices();
			?>
		<?php
	}
}

if ( ! function_exists( 'logistica_admin_subnav' ) ) {
	/**
	 * Prints quick navigation between plugin sections.
	 *
	 * @return void
	 */
	function logistica_admin_subnav() {
		logistica_nav_tabs( 'admin' );
	}
}

if ( ! function_exists( 'logistica_tab_loading_overlay' ) ) {
	/**
	 * Overlay de carga reutilizable para pestañas y vistas pesadas.
	 *
	 * @param string $message Texto visible mientras carga.
	 * @param bool   $visible Si debe mostrarse al renderizar.
	 * @return void
	 */
	function logistica_tab_loading_overlay( $message = '', $visible = true ) {
		if ( '' === $message ) {
			$message = __( 'Cargando…', 'logistica' );
		}
		?>
		<div
			class="logistica-tab-loading"
			data-logistica-tab-loading
			role="status"
			aria-live="polite"
			aria-busy="<?php echo $visible ? 'true' : 'false'; ?>"
			<?php echo $visible ? '' : ' hidden'; ?>
		>
			<div class="logistica-tab-loading__inner">
				<span class="logistica-tab-loading__spinner" aria-hidden="true"></span>
				<p data-logistica-tab-loading-message><?php echo esc_html( $message ); ?></p>
			</div>
		</div>
		<?php
	}
}

if ( ! function_exists( 'logistica_tab_loading_message' ) ) {
	/**
	 * Mensaje de carga por slug de pestaña.
	 *
	 * @param string $tab_slug Tab slug.
	 * @return string
	 */
	function logistica_tab_loading_message( $tab_slug ) {
		$messages = array(
			'logistica'                        => __( 'Cargando tareas y recordatorios…', 'logistica' ),
			'logistica-conversiones'           => __( 'Cargando calculadoras…', 'logistica' ),
			'logistica-calculo-tapas'          => __( 'Cargando cálculo de tapas…', 'logistica' ),
			'logistica-calculos'               => __( 'Cargando cálculos de bolsas…', 'logistica' ),
			'logistica-inventario-bodega'      => __( 'Cargando inventario de bodega…', 'logistica' ),
			'logistica-cierre-ordenes'         => __( 'Cargando cierre de órdenes…', 'logistica' ),
			'logistica-codigos-etiqueta'       => __( 'Cargando códigos de etiqueta…', 'logistica' ),
			'logistica-revision-minuta'        => __( 'Cargando revisión de minutas…', 'logistica' ),
			'logistica-rotulos-rollos'         => __( 'Cargando rótulos de rollos…', 'logistica' ),
			'logistica-cuadro-pedidos'         => __( 'Cargando cuadro de pedidos…', 'logistica' ),
			'logistica-planeacion-impresion'   => __( 'Cargando planeación de impresión…', 'logistica' ),
			'logistica-planeacion-formado'     => __( 'Cargando planeación de formado…', 'logistica' ),
			'logistica-cierre-caracteristicas' => __( 'Cargando características…', 'logistica' ),
		);

		return $messages[ $tab_slug ] ?? __( 'Cargando…', 'logistica' );
	}
}

if ( ! function_exists( 'logistica_notice' ) ) {
	/**
	 * Prints a dismissible success or info notice.
	 *
	 * @param string $message Notice text.
	 * @param string $type    Notice type: success, info, warning.
	 * @return void
	 */
	function logistica_notice( $message, $type = 'success' ) {
		$allowed = array( 'success', 'info', 'warning' );
		$type    = in_array( $type, $allowed, true ) ? $type : 'success';
		?>
		<div class="logistica-notice logistica-notice--<?php echo esc_attr( $type ); ?>" role="status" data-logistica-notice>
			<p><?php echo esc_html( $message ); ?></p>
			<button type="button" class="logistica-notice-close" aria-label="<?php esc_attr_e( 'Cerrar aviso', 'logistica' ); ?>">&times;</button>
		</div>
		<?php
	}
}

if ( ! function_exists( 'logistica_render_tab_panel_inner' ) ) {
	/**
	 * Renderiza el contenido interno de una pestaña (operaciones o config).
	 *
	 * @param string $slug    Tab slug.
	 * @param string $context operations|config.
	 * @return string HTML o vacío si no hay vista.
	 */
	function logistica_render_tab_panel_inner( $slug, $context = 'operations' ) {
		$slug    = sanitize_key( (string) $slug );
		$context = Logistica_Tabs::normalize_context( $context );

		ob_start();

		$GLOBALS['logistica_tabbed_shell'] = true;

		if ( Logistica_Tabs::CONTEXT_CONFIG === $context ) {
			$view_path = Logistica_Tabs::config_tab_path( $slug );
			if ( is_readable( $view_path ) ) {
				$all_tab_labels             = logistica_get_nav_tabs();
				$logistica_config_tab_label = $all_tab_labels[ $slug ] ?? $slug;
				include $view_path;
				unset( $logistica_config_tab_label );
			}
		} else {
			$tab_views = logistica_get_tab_views();
			$view_file = $tab_views[ $slug ] ?? '';
			$view_path = $view_file ? logistica_view_path( $view_file ) : '';
			if ( $view_path && is_readable( $view_path ) ) {
				include $view_path;
			}
		}

		unset( $GLOBALS['logistica_tabbed_shell'] );

		return (string) ob_get_clean();
	}
}

if ( ! function_exists( 'logistica_render_select_catalogs_for_tab' ) ) {
	/**
	 * Renderiza catálogos de selects para una pestaña de configuración.
	 *
	 * @param string $tab_slug    Tab slug.
	 * @param string $intro_title Optional title.
	 * @param string $intro_text  Optional description.
	 * @return void
	 */
	function logistica_render_select_catalogs_for_tab( $tab_slug, $intro_title = '', $intro_text = '' ) {
		if ( ! class_exists( 'Logistica_Select_Catalog', false ) ) {
			return;
		}

		$tab_slug    = sanitize_key( (string) $tab_slug );
		$by_tab      = Logistica_Select_Catalog::catalogs_by_config_tab();
		$catalog_ids = $by_tab[ $tab_slug ] ?? array();

		if ( empty( $catalog_ids ) ) {
			return;
		}

		if ( '' === $intro_title ) {
			$intro_title = __( 'Listas desplegables', 'logistica' );
		}
		if ( '' === $intro_text ) {
			$intro_text = __( 'Administra las opciones de los selects de esta pestaña. Edita, agrega o elimina con los botones de cada fila.', 'logistica' );
		}

		include LOGISTICA_PLUGIN_DIR . 'admin/views/partials/select-catalog-options.php';
	}
}

if ( ! function_exists( 'logistica_etiqueta_options_icon_actions' ) ) {
	/**
	 * Botones de acción con icono para filas del catálogo de etiquetas (backend).
	 *
	 * @param string $kind        tipo|mercado|tamano.
	 * @param bool   $can_delete  Mostrar botón eliminar.
	 * @return void
	 */
	function logistica_etiqueta_options_icon_actions( $kind, $can_delete = true ) {
		unset( $can_delete );
		?>
		<div class="logistica-etiqueta-catalog-options__actions">
			<button type="button" class="button button-small logistica-table-icon-btn logistica-etiqueta-catalog-options__btn-save" data-etiqueta-options-save="<?php echo esc_attr( $kind ); ?>" aria-label="<?php esc_attr_e( 'Guardar', 'logistica' ); ?>" title="<?php esc_attr_e( 'Guardar', 'logistica' ); ?>">
				<span class="dashicons dashicons-saved" aria-hidden="true"></span>
			</button>
			<button type="button" class="button button-small logistica-table-icon-btn logistica-etiqueta-catalog-options__btn-delete" data-etiqueta-options-delete="<?php echo esc_attr( $kind ); ?>" aria-label="<?php esc_attr_e( 'Eliminar', 'logistica' ); ?>" title="<?php esc_attr_e( 'Eliminar', 'logistica' ); ?>">
				<span class="dashicons dashicons-trash" aria-hidden="true"></span>
			</button>
		</div>
		<?php
	}
}

if ( ! function_exists( 'logistica_etiqueta_option_kind_label' ) ) {
	/**
	 * Etiqueta legible del tipo de opción de catálogo.
	 *
	 * @param string $kind tipo|mercado|tamano.
	 * @return string
	 */
	function logistica_etiqueta_option_kind_label( $kind ) {
		$labels = array(
			'tipo'    => __( 'Tipo', 'logistica' ),
			'mercado' => __( 'Mercado', 'logistica' ),
			'tamano'  => __( 'Tamaño', 'logistica' ),
		);

		return isset( $labels[ $kind ] ) ? (string) $labels[ $kind ] : (string) $kind;
	}
}

if ( ! function_exists( 'logistica_etiqueta_options_restore_button' ) ) {
	/**
	 * Botón restaurar para filas ocultas del catálogo.
	 *
	 * @param string $kind tipo|mercado|tamano.
	 * @return void
	 */
	function logistica_etiqueta_options_restore_button( $kind ) {
		?>
		<button type="button" class="button button-small logistica-table-icon-btn logistica-etiqueta-catalog-options__btn-restore" data-etiqueta-options-restore="<?php echo esc_attr( $kind ); ?>" aria-label="<?php esc_attr_e( 'Restaurar', 'logistica' ); ?>" title="<?php esc_attr_e( 'Restaurar', 'logistica' ); ?>">
			<span class="dashicons dashicons-undo" aria-hidden="true"></span>
		</button>
		<?php
	}
}

if ( ! function_exists( 'logistica_field_hint' ) ) {
	/**
	 * Prints helper text below a form field.
	 *
	 * @param string $text Hint text.
	 * @return void
	 */
	function logistica_field_hint( $text ) {
		echo '<span class="logistica-field-hint">' . esc_html( $text ) . '</span>';
	}
}

if ( ! function_exists( 'logistica_table_filters_icon' ) ) {
	/**
	 * Icono embudo para el bloque de filtros de tabla.
	 *
	 * @return void
	 */
	function logistica_table_filters_icon() {
		?>
		<span class="logistica-tapas-filters__icon" aria-hidden="true">
			<svg width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
				<path d="M4 5h16l-6.2 7.35v5.9L10 20v-7.65L4 5z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/>
			</svg>
		</span>
		<?php
	}
}

if ( ! function_exists( 'logistica_table_filters_clear_icon' ) ) {
	/**
	 * Icono X para limpiar filtros.
	 *
	 * @return void
	 */
	function logistica_table_filters_clear_icon() {
		?>
		<span class="logistica-tapas-filters__btn-icon" aria-hidden="true">
			<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
				<path d="M6 6l12 12M18 6L6 18" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
			</svg>
		</span>
		<?php
	}
}

if ( ! function_exists( 'logistica_page_toolbar_intro' ) ) {
	/**
	 * Encabezado principal con icono, titulo y descripcion.
	 *
	 * @param string $title       Titulo de la vista.
	 * @param string $description Texto descriptivo.
	 * @param string $icon        Clase dashicons (ej. dashicons-store).
	 * @return void
	 */
	function logistica_page_toolbar_intro( $title, $description, $icon = 'dashicons-admin-generic' ) {
		?>
		<div class="logistica-page-toolbar__intro">
			<div class="logistica-page-toolbar__badge" aria-hidden="true">
				<span class="dashicons <?php echo esc_attr( $icon ); ?>"></span>
			</div>
			<div>
				<h2><?php echo esc_html( $title ); ?></h2>
				<?php if ( '' !== $description ) : ?>
					<p><?php echo esc_html( $description ); ?></p>
				<?php endif; ?>
			</div>
		</div>
		<?php
	}
}

if ( ! function_exists( 'logistica_page_toolbar_icon' ) ) {
	/**
	 * Icono del encabezado principal para layouts personalizados.
	 *
	 * @param string $icon Clase dashicons.
	 * @return void
	 */
	function logistica_page_toolbar_icon( $icon = 'dashicons-admin-generic' ) {
		?>
		<div class="logistica-page-toolbar__badge" aria-hidden="true">
			<span class="dashicons <?php echo esc_attr( $icon ); ?>"></span>
		</div>
		<?php
	}
}

if ( ! function_exists( 'logistica_table_excel_view_header' ) ) {
	/**
	 * Titulo e intro de la vista tabla tipo Excel.
	 *
	 * @param string $intro Texto descriptivo bajo el titulo.
	 * @return void
	 */
	function logistica_table_excel_view_header( $intro = '' ) {
		if ( '' === $intro ) {
			$intro = __( 'Vista tipo Excel. Usa los filtros para acotar los registros visibles en la tabla.', 'logistica' );
		}
		?>
		<h5 class="logistica-tapas-section__title"><?php esc_html_e( 'Vista hoja de cálculo (Excel)', 'logistica' ); ?></h5>
		<p class="logistica-tapas-excel__intro"><?php echo esc_html( $intro ); ?></p>
		<?php
	}
}

if ( ! function_exists( 'logistica_table_status_legend' ) ) {
	/**
	 * Leyenda de colores de filas/celdas de la tabla.
	 *
	 * @param array<int, array{class:string, label:string}> $items Clase swatch y etiqueta.
	 * @return void
	 */
	function logistica_table_status_legend( $items ) {
		if ( empty( $items ) || ! is_array( $items ) ) {
			return;
		}
		?>
		<ul class="logistica-tapas-status-legend" aria-label="<?php esc_attr_e( 'Significado de colores', 'logistica' ); ?>">
			<?php foreach ( $items as $item ) : ?>
				<li>
					<span class="logistica-tapas-status-legend__swatch <?php echo esc_attr( (string) ( $item['class'] ?? '' ) ); ?>"></span>
					<?php echo esc_html( (string) ( $item['label'] ?? '' ) ); ?>
				</li>
			<?php endforeach; ?>
		</ul>
		<?php
	}
}

if ( ! function_exists( 'logistica_table_sheet_actions' ) ) {
	/**
	 * Boton de accion antes de la tabla (mismo estilo que calculo tapas/bolsas).
	 *
	 * @param string $label  Texto del boton.
	 * @param string $action Valor data-action.
	 * @return void
	 */
	function logistica_table_sheet_actions( $label, $action ) {
		?>
		<div class="logistica-table-sheet-actions">
			<button type="button" class="button logistica-sheet-action-btn" data-action="<?php echo esc_attr( $action ); ?>">
				<?php echo esc_html( $label ); ?>
			</button>
		</div>
		<?php
	}
}

if ( ! function_exists( 'logistica_table_pagination' ) ) {
	/**
	 * Controles de paginacion para tablas del plugin.
	 *
	 * @param string $prefix Prefijo data-* (warehouse|cierre-car).
	 * @return void
	 */
	function logistica_table_pagination( $prefix ) {
		?>
		<nav class="logistica-table-pagination" data-<?php echo esc_attr( $prefix ); ?>-pagination aria-label="<?php esc_attr_e( 'Paginación de tabla', 'logistica' ); ?>">
			<p class="logistica-table-pagination__info" data-<?php echo esc_attr( $prefix ); ?>-page-info></p>
			<div class="logistica-table-pagination__controls">
				<button type="button" class="button logistica-table-pagination__btn" data-action="<?php echo esc_attr( $prefix ); ?>-page-prev" disabled>
					<?php esc_html_e( 'Anterior', 'logistica' ); ?>
				</button>
				<span class="logistica-table-pagination__label" data-<?php echo esc_attr( $prefix ); ?>-page-label></span>
				<button type="button" class="button logistica-table-pagination__btn" data-action="<?php echo esc_attr( $prefix ); ?>-page-next" disabled>
					<?php esc_html_e( 'Siguiente', 'logistica' ); ?>
				</button>
			</div>
			<label class="logistica-table-pagination__size">
				<span><?php esc_html_e( 'Filas por página', 'logistica' ); ?></span>
				<select data-<?php echo esc_attr( $prefix ); ?>-page-size>
					<option value="10">10</option>
					<option value="25" selected>25</option>
					<option value="50">50</option>
					<option value="100">100</option>
				</select>
			</label>
		</nav>
		<?php
	}
}

if ( ! function_exists( 'logistica_minuta_copy_input' ) ) {
	/**
	 * Campo de solo lectura para copiar el valor al hacer clic.
	 *
	 * @param string $value   Valor visible.
	 * @param string $label   Etiqueta accesible.
	 * @param bool   $is_num  Si es columna numérica.
	 * @return void
	 */
	function logistica_minuta_copy_input( $value, $label, $is_num = false ) {
		$classes = 'logistica-tapas-excel-copy-input logistica-minutas-copy-input';
		if ( $is_num ) {
			$classes .= ' is-num';
		}
		?>
		<input
			type="text"
			readonly
			tabindex="0"
			class="<?php echo esc_attr( $classes ); ?>"
			value="<?php echo esc_attr( (string) $value ); ?>"
			title="<?php esc_attr_e( 'Clic para copiar', 'logistica' ); ?>"
			aria-label="<?php echo esc_attr( $label ); ?>"
		>
		<?php
	}
}

if ( ! function_exists( 'logistica_table_excel_toolbar' ) ) {
	/**
	 * Barra Vista + exportar/importar Excel (mismo bloque que calculo bolsas).
	 *
	 * @param array<string, mixed> $config Configuración de export/import.
	 * @return void
	 */
	function logistica_table_excel_toolbar( $config = array() ) {
		$config = wp_parse_args(
			$config,
			array(
				'export_url'           => '',
				'import_form_id'       => '',
				'import_input_id'      => '',
				'import_input_name'    => '',
				'import_data_attr'     => '',
				'import_action'        => '',
				'show_zoom'            => true,
			)
		);
		?>
		<div class="logistica-tapas-excel-toolbar">
			<?php if ( ! empty( $config['show_zoom'] ) ) : ?>
				<?php logistica_excel_table_zoom_controls(); ?>
			<?php endif; ?>
			<?php if ( ! empty( $config['export_url'] ) ) : ?>
				<a class="button logistica-excel-btn logistica-excel-btn--export" href="<?php echo esc_url( (string) $config['export_url'] ); ?>">
					<span class="dashicons dashicons-media-spreadsheet" aria-hidden="true"></span>
					<span class="logistica-excel-btn__label"><?php esc_html_e( 'Exportar Excel (.xlsx)', 'logistica' ); ?></span>
				</a>
			<?php endif; ?>
			<?php if ( ! empty( $config['import_action'] ) && ! empty( $config['import_form_id'] ) ) : ?>
				<input
					<?php if ( ! empty( $config['import_input_id'] ) ) : ?>
						id="<?php echo esc_attr( (string) $config['import_input_id'] ); ?>"
					<?php endif; ?>
					form="<?php echo esc_attr( (string) $config['import_form_id'] ); ?>"
					type="file"
					<?php if ( ! empty( $config['import_input_name'] ) ) : ?>
						name="<?php echo esc_attr( (string) $config['import_input_name'] ); ?>"
					<?php endif; ?>
					<?php if ( ! empty( $config['import_data_attr'] ) ) : ?>
						<?php echo 'data-' . esc_attr( (string) $config['import_data_attr'] ) . '=""'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
					<?php endif; ?>
					accept=".xls,.xlsx,.csv,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,text/csv"
					hidden
				>
				<button type="button" class="button logistica-excel-btn logistica-excel-btn--import" data-action="<?php echo esc_attr( (string) $config['import_action'] ); ?>">
					<span class="dashicons dashicons-upload" aria-hidden="true"></span>
					<span class="logistica-excel-btn__label"><?php esc_html_e( 'Importar Excel', 'logistica' ); ?></span>
				</button>
			<?php endif; ?>
		</div>
		<?php
	}
}

if ( ! function_exists( 'logistica_excel_table_zoom_controls' ) ) {
	/**
	 * Botones +/- para ampliar o reducir la vista de la tabla Excel.
	 *
	 * @return void
	 */
	function logistica_excel_table_zoom_controls() {
		?>
		<div class="logistica-excel-zoom" data-excel-zoom>
			<span class="logistica-excel-zoom__label"><?php esc_html_e( 'Vista', 'logistica' ); ?></span>
			<button type="button" class="logistica-excel-zoom__btn" data-action="excel-zoom-out" aria-label="<?php esc_attr_e( 'Reducir tabla', 'logistica' ); ?>">−</button>
			<span class="logistica-excel-zoom__value" data-excel-zoom-label aria-live="polite">100%</span>
			<button type="button" class="logistica-excel-zoom__btn" data-action="excel-zoom-in" aria-label="<?php esc_attr_e( 'Ampliar tabla', 'logistica' ); ?>">+</button>
			<button
				type="button"
				class="logistica-excel-zoom__btn logistica-excel-zoom__btn--density"
				data-action="excel-density-toggle"
				aria-pressed="false"
				title="<?php esc_attr_e( 'Modo compacto: filas más densas', 'logistica' ); ?>"
				aria-label="<?php esc_attr_e( 'Modo compacto', 'logistica' ); ?>"
			>
				<span class="dashicons dashicons-editor-table" aria-hidden="true"></span>
			</button>
		</div>
		<?php
	}
}

if ( ! function_exists( 'logistica_excel_table_zoom_toolbar' ) ) {
	/**
	 * Controles Vista (+/-) anclados al encabezado de la seccion.
	 *
	 * @return void
	 */
	function logistica_excel_table_zoom_toolbar() {
		?>
		<div class="logistica-tapas-excel-toolbar logistica-excel-zoom-toolbar">
			<?php logistica_excel_table_zoom_controls(); ?>
		</div>
		<?php
	}
}

if ( ! function_exists( 'logistica_admin_footer' ) ) {
	/**
	 * Closes the shared admin wrapper.
	 *
	 * @return void
	 */
	function logistica_admin_footer() {
		if ( logistica_is_tabbed_shell() ) {
			return;
		}
		echo '</div></div>';
	}
}

if ( ! function_exists( 'logistica_format_cell' ) ) {
	/**
	 * Formats a table cell value with badges when useful.
	 *
	 * @param string $key   Column key.
	 * @param mixed  $value Cell value.
	 * @return string
	 */
	function logistica_format_cell( $key, $value ) {
		$value = (string) ( $value ?? '' );

		if ( '' === $value ) {
			return '';
		}

		if ( 'estado' === $key ) {
			$class = sanitize_html_class( strtolower( $value ) );
			return '<span class="logistica-badge logistica-badge--' . esc_attr( $class ) . '">' . esc_html( $value ) . '</span>';
		}

		if ( 'tipo_movimiento' === $key ) {
			$class = sanitize_html_class( strtolower( $value ) );
			return '<span class="logistica-badge logistica-badge--mov logistica-badge--' . esc_attr( $class ) . '">' . esc_html( $value ) . '</span>';
		}

		return esc_html( $value );
	}
}

if ( ! function_exists( 'logistica_recent_table' ) ) {
	/**
	 * Prints a compact table for recent rows.
	 *
	 * @param array $rows Rows to print.
	 * @param array $columns Columns as key => label.
	 * @return void
	 */
	function logistica_recent_table( $rows, $columns ) {
		?>
		<div class="logistica-table-wrap">
			<table class="widefat striped logistica-table">
				<thead>
					<tr>
						<?php foreach ( $columns as $label ) : ?>
							<th><?php echo esc_html( $label ); ?></th>
						<?php endforeach; ?>
					</tr>
				</thead>
				<tbody>
					<?php if ( empty( $rows ) ) : ?>
						<tr>
							<td colspan="<?php echo esc_attr( count( $columns ) ); ?>" class="logistica-empty-cell">
								<span class="logistica-empty-icon" aria-hidden="true">—</span>
								<strong><?php esc_html_e( 'Sin registros todavía', 'logistica' ); ?></strong>
								<span><?php esc_html_e( 'Completa el formulario de la izquierda para crear el primero.', 'logistica' ); ?></span>
							</td>
						</tr>
					<?php else : ?>
						<?php foreach ( $rows as $row ) : ?>
							<tr>
								<?php foreach ( $columns as $key => $label ) : ?>
									<td><?php echo logistica_format_cell( $key, $row[ $key ] ?? '' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></td>
								<?php endforeach; ?>
							</tr>
						<?php endforeach; ?>
					<?php endif; ?>
				</tbody>
			</table>
		</div>
		<?php
	}
}
