<?php
/**
 * WeCodeArt Framework
 *
 * WARNING: This file is part of the core WeCodeArt Framework. DO NOT edit this file under any circumstances.
 * Please do all modifications in the form of a child theme.
 *
 * @package		WeCodeArt Framework
 * @subpackage  Gutenberg\Blocks
 * @copyright   Copyright (c) 2025, WeCodeArt Framework
 * @since		5.0.0
 * @version		6.6.5
 */

namespace WeCodeArt\Gutenberg\Blocks;

defined( 'ABSPATH' ) || exit;

use WeCodeArt\Singleton;
use WeCodeArt\Config\Traits\Asset;
use WeCodeArt\Gutenberg\Blocks\Dynamic;
// use function unset;
// use function isset;
// use function empty;
use function end;
use function join;
use function explode;
use function sprintf;
use function is_null;
use function is_array;
use function current;
use function array_merge;
use function array_filter;
use function array_key_exists;
use function add_filter;
use function parse_blocks;
use function wp_unique_id;
use function wp_style_is;
use function wp_script_is;
use function wp_register_style;
use function wp_enqueue_style;
use function wp_enqueue_script;
use function wp_add_inline_style;
use function wp_parse_args;
use function wp_list_filter;
use function strpos;
use function preg_match;
use function str_replace;
use function sanitize_html_class;
use function block_core_navigation_filter_out_empty_blocks;
use function WeCodeArt\Functions\{ get_prop, get_json_color, get_lightness_limit, array_extract };

/**
 * Gutenberg Navigation block.
 */
class Navigation extends Dynamic {

	use Singleton;
	use Asset;

	/**
	 * Block static.
	 *
	 * @var mixed
	 */
	protected static $responsive_loaded = null;
	protected static $themes_loaded = [];

	/**
	 * Block namespace.
	 *
	 * @var string
	 */
	protected $namespace = 'core';

	/**
	 * Block name.
	 *
	 * @var string
	 */
	protected $block_name = 'navigation';

	/**
	 * Init.
	 */
	public function init() {
		\register_block_style( $this->get_block_type(), [
			'name' 	=> 'pills',
            'label'	=> __( 'Pills', 'wecodeart' ),
			'inline_style' => static::get_extra_styles( 'pills' )
		] );

		\add_filter( 'block_core_navigation_render_fallback', [ $this, 'fallback' ] );
	}

	/**
	 * Block args.
	 *
	 * @return 	array
	 */
	public function block_type_args(): array {
		return [
			'render_callback' 		=> [ $this, 'render' ],
			'view_script_handles'	=> []
		];
	}

	/**
	 * Dynamically renders the `core/navigation` block.
	 *
	 * @param 	array 	$attributes	The attributes.
	 * @param 	string 	$content 	The block markup.
	 * @param 	string 	$block 		The block data.
	 *
	 * @return 	string 	The block markup.
	 */
	public function render( array $attributes = [], string $content = '', $block = null ): string {
		if ( $color = get_prop( $attributes, 'rgbTextColor' ) ) {
			$attributes['customTextColor'] = $color;
		}
		
		if ( $color = get_prop( $attributes, 'rgbBackgroundColor' ) ) {
			$attributes['customBackgroundColor'] = $color;
		}

		unset( $attributes['rgbTextColor'], $attributes['rgbBackgroundColor'] );

		$inner_blocks = $block->inner_blocks;
		
		// Ensure that blocks saved with the legacy ref attribute name (navigationMenuId) continue to render.
		if ( array_key_exists( 'navigationMenuId', $attributes ) ) {
			$attributes['ref'] = $attributes['navigationMenuId'];
		}

		// Load inner blocks from the navigation post.
		if ( array_key_exists( 'ref', $attributes ) ) {
			$navigation_post = get_post( $attributes['ref'] );
			
			if ( isset( $navigation_post ) ) { 
				$parsed_blocks = parse_blocks( $navigation_post->post_content );
				$parsed_blocks = block_core_navigation_filter_out_empty_blocks( $parsed_blocks );
				
				$inner_blocks = new \WP_Block_List( $parsed_blocks, $attributes );
			}
		}
		
		// If there are no inner blocks then fallback to rendering an appropriate fallback.
		if ( empty( $inner_blocks ) ) {
			$parsed_blocks 	= block_core_navigation_get_fallback_blocks();

			// Fallback might have been filtered so do basic test for validity.
			if ( empty( $parsed_blocks ) || ! is_array( $parsed_blocks ) ) {
				return '';
			}

			$inner_blocks = new \WP_Block_List( $parsed_blocks, $attributes );
		}

		$block_id	= wp_unique_id( 'wp-navigation-' );

		// Offcanvas classes
		$breakpoint = wecodeart_json( [ 'settings', 'custom', 'mobileBreakpoint' ], 'lg' );
		$breakpoint = [ $breakpoint . ':wp-offcanvas--expand' ];
		$hidden   	= get_prop( $attributes, [ 'overlayMenu' ] ) === 'always';
		$classes	= explode( ' ', get_prop( $attributes, [ 'className' ], '' ) );
		$expanded   = array_extract( $classes, ':wp-offcanvas--expand' ); 	// Extract expand modifier class
		$expanded   = $expanded ?: ( $hidden ? [] : $breakpoint );			// Default expand modifier if not set
		$offcanvas  = array_extract( $classes, 'wp-offcanvas--' );			// Extract other modifiers (top|start|end|bottom|other)
		$offcanvas  = $offcanvas ?: [ 'wp-offcanvas--start' ];				// Default position if not set
		$offcanvas 	= array_merge( $offcanvas, $expanded );					// Merge offcanvas classes

		if( get_prop( $attributes, [ 'overlayMenu' ] ) === 'always' ) {

		}
		
		// Attributes
		$attributes['className'] = join( ' ', $classes );
		$attrs		= $this->get_wrapper_attributes( $attributes );

		// This CSS holds the block customization.
		if( ! wp_style_is( $this->get_asset_handle() . '-custom' ) ) {
			\wp_register_style( $this->get_asset_handle() . '-custom', '', [ $this->get_asset_handle() ], wecodeart( 'version' ) );
			\wp_enqueue_style( $this->get_asset_handle() . '-custom' );
		}

		// Add Expand CSS.
		$classname = get_prop( $attrs, [ 'class' ], '' );
		if( strpos( $classname, 'navbar-expand' ) !== false ) {
			\wp_add_inline_style( $this->get_asset_handle() . '-custom', $this->get_responsive_styles( 'expand' ) );
		}

		// Add Themes CSS.
		preg_match( '/navbar-(light|dark)/i', $classname, $navbar_theme );
		if( count( $navbar_theme ) && ! in_array( current( $navbar_theme ), self::$themes_loaded ) ) {

			\wp_add_inline_style( $this->get_asset_handle() . '-custom', $this->get_color_styles( current( $navbar_theme ) ) );
			
			self::$themes_loaded[] = current( $navbar_theme );
		}

		return wecodeart( 'dom' )::wrap( 'navbar', [ [
			'tag' 	=> 'nav',
			'attrs'	=> $attrs
		] ], function( $attributes, $inner_blocks, $block_id, $offcanvas ) {

			// Navbar List HTML
			$html = wecodeart( 'dom' )::wrap( 'navbar-nav', [ [
				'tag' 	=> 'ul',
				'attrs' => [
					'class' => 'wp-block-navigation__container navbar-nav',
				]
			] ], function( $inner_blocks ) {
				foreach( $inner_blocks as $inner_block ) echo $this->render_menu_block( $inner_block );
			}, [ $inner_blocks ], false );

			// Is responsive? Render in offcanvas container
			if( get_prop( $attributes, 'overlayMenu' ) !== 'never' ) {

				// Styles
				if( is_null( self::$responsive_loaded ) ) {

					\wp_add_inline_style( 'wp-block-' . $this->block_name . '-custom', $this->get_responsive_styles() );
					
					self::$responsive_loaded = true;
				}

				// Toggler
				wecodeart_template( 'general/toggler', [
					'id'		=> $block_id,
					'class'		=> 'navbar-toggler',
					'toggle' 	=> 'offcanvas',
					'icon'		=> get_prop( $attributes, 'hasIcon' )
				] );

				// OffCanvas
				wecodeart_template( 'general/offcanvas', [
					'id'		=> $block_id,
					'class'		=> $offcanvas,
					'content' 	=> $html,
				] );

				return;
			}

			// Is ok, we use the WP block rendering
			echo $html;

		}, [ $attributes, $inner_blocks, $block_id, $offcanvas ], false );
	}

	/**
	 * Renders menu item wrapper.
	 *
	 * @param 	object 	$block.
	 *
	 * @return 	string 	Rendered block with wrapper block
	 */
	public function render_menu_block( $block ) {
		$html = $block->render();
		
		// For this specific blocks, please wrap them in a <li> for valid markup
		if( in_array( get_prop( $block->parsed_block, 'blockName', '' ), [
			'core/buttons',
			'core/spacer',
			'core/search',
			'core/social-links',
			'core/site-title',
			'core/site-logo',
			'core/loginout',
		] ) ) {
			$classes	= [ 'wp-block-navigation-item', 'nav-item' ];
			$classes[]  = 'nav-item--' . join( '-', explode( '/', get_prop( $block->parsed_block, 'blockName', '' ) ) );

			return wecodeart( 'dom' )::wrap( 'nav-item', [ [
				'tag' 	=> 'li',
				'attrs'	=> [
					'class'	=> join( ' ', $classes ),
				]
			] ], $html, [], false );
		}

		return $html;
	}

	/**
	 * Get Class Color.
	 *
	 * @param 	array 	$attributes	Attributes
	 * @param 	string 	$key		Attribute name
	 * 
	 * @return 	string 	HEX color code for pallete class
	 */
	public static function get_class_color( $attributes, $key = 'background' ) {
		$palette 	= wecodeart_json( [ 'settings', 'color', 'palette' ], [] );
				
		$_keys 		= [
			'overlay-background' 	=> 'overlayBackgroundColor',
			'overlay-text' 			=> 'overlayTextColor',
			'custom-background'		=> 'customBackgroundColor',
			'background' 			=> 'backgroundColor',
			'text'		 			=> 'textColor',
		];

		// Real attribute name.
		$attribute 	= isset( $_keys[$key] ) ? $_keys[$key] : $key;

		// Get custom
		$color 		= get_prop( $attributes, [ 'style', 'color', $attribute ] );
		
		// If not custom, get named.
		if( $color === null ) {
			$color 	= get_prop( $attributes, $attribute, $color );
			$color 	= get_prop( current( wp_list_filter( $palette, [ 'slug' => $color ] ) ), 'color', false );
		}

		// If named not found, fallback to body.
		if( $color === false ) {
			$color 	= get_json_color( [ 'styles', 'color', $key ] );
		}

		return $color;
	}

	/**
	 * Build an array with CSS classes and inline styles defining the colors
	 * which will be applied to the navigation markup in the front-end.
	 *
	 * @param  array $attributes 	Navigation block context.
	 * @return array  	 			CSS classes and inline styles.
	 */
	public function get_colors( $attributes ) {
		$colors = [
			'classes'   => [],
			'styles' 	=> '',
		];
	
		// Text color.
		$has_named_text_color  = get_prop( $attributes, 'textColor', false );
		$has_custom_text_color = get_prop( $attributes, 'customTextColor', false );

		// Overlays - added via CSS because of extra specificity
		
		// Background color.
		$has_named_background_color  = get_prop( $attributes, 'backgroundColor', false );
		$has_custom_background_color = get_prop( $attributes, 'customBackgroundColor', false );
	
		// If has text color.
		if ( $has_custom_text_color || $has_named_text_color ) {
			// Add has-text-color class.
			$colors['classes'][] = 'has-text-color';
		}
	
		if ( $has_named_text_color ) {
			// Add the color class.
			$colors['classes'][] = sprintf( 'has-%s-color', $has_named_text_color );
		} elseif ( $has_custom_text_color ) {
			// Add the custom color inline style.
			$colors['styles'] .= sprintf( 'color: %s;', $has_custom_text_color );
			$colors['styles'] .= sprintf( '--wp--custom--color: %s;', $has_custom_text_color );
		}
	
		// If has background color.
		if ( $has_custom_background_color || $has_named_background_color ) {
			// Add has-background class.
			$colors['classes'][] = 'has-background';
		}
	
		if ( $has_named_background_color ) {
			// Add the background-color class.
			$colors['classes'][] = sprintf( 'has-%s-background-color', $has_named_background_color );
		} elseif ( $has_custom_background_color ) {
			// Add the custom background-color inline style.
			$colors['styles'] .= sprintf( 'background-color: %s;', $has_custom_background_color );
			$colors['styles'] .= sprintf( '--wp--custom--background-color: %s;', $has_custom_background_color );
		}
	
		return $colors;
	}

	/**
	 * Build an array with CSS classes and inline styles defining the font sizes
	 * which will be applied to the navigation markup in the front-end.
	 *
	 * @param  array $attributes 	Navigation block context.
	 * @return array 				Font size CSS classes and inline styles.
	 */
	public function get_typography( $attributes ) {
		// CSS classes.
		$typography = [
			'classes'   => [],
			'styles' 	=> '',
		];

		if ( $value = get_prop( $attributes, 'fontSize' ) ) {
			// Add the font size class.
			$typography['classes'][] = sprintf( 'has-%s-font-size', $value );
		} elseif ( $value = get_prop( $attributes, 'customFontSize' ) ) {
			// Add the custom font size inline style.
			$typography['styles'] = sprintf( 'font-size: %spx;--wp--custom--font-size: %spx;', $value, $value );
		}

		return $typography;
	}

	/**
	 * Return an array of wrapper attributes.
	 * 
	 * @return 	array
	 */
	public function get_wrapper_attributes( $attributes ) {
		$colors     = $this->get_colors( $attributes );
		$typography = $this->get_typography( $attributes );
		$background = get_prop( $attributes, 'customBackgroundColor', self::get_class_color( $attributes ) );
		$background = wecodeart( 'styles' )::hex_to_rgb( $background, 1, true );
		$luminance 	= wecodeart( 'styles' )::rgb_luminance( $background );
		$classnames = explode( ' ',  get_prop( $attributes, 'className', '' ) );

		$classes 	= [ 'wp-block-navigation', 'navbar' ];
		$excludes 	= [ 'navbar-light', 'navbar-dark' ];

		// Detect navbar theme
		if( count( array_intersect( $classnames, $excludes ) ) === 0 ) {
			$classes[] = ( $luminance < get_lightness_limit() ) ? 'navbar-dark' : 'navbar-light';
		}

		// Navbar orientation
		if( get_prop( $attributes, [ 'layout', 'orientation' ] ) === 'horizontal' ) {
			$classes[] = 'navbar-expand';
		}
		
		// Offcanvas
		if( $value = get_prop( $attributes, 'overlayMenu' ) ) {
			if( $value !== 'never' ) {
				$classes 	= array_diff( $classes, [ 'navbar-expand' ] );
			}

			if( $value === 'mobile' ) {
				$classes[] 	= 'navbar-expand-' . wecodeart_json( [ 'settings', 'custom', 'mobileBreakpoint' ], 'lg' );
			}
		}
		
		// Hover or click
		if( ! get_prop( $attributes, 'openSubmenusOnClick' ) ) {
			$classes[] 	= 'with-hover';
		}

		// Hide dropdown toggle
		if( get_prop( $attributes, 'showSubmenuIcon', false ) === false ) {
			$classes[] 	= 'hide-toggle';
		}

		$classes    	= array_merge( $classes, $classnames, $colors['classes'], $typography['classes'] );
		$block_styles 	= get_prop( $attributes, 'styles', '' );

		return [
			'class'	=> join( ' ', array_filter( $classes ) ),
			'style'	=> $block_styles . $colors['styles'] . $typography['styles'],
		];
	}

	/**
	 * Fallback arguments.
	 *
	 * @return array
	 */
	public function fallback( $blocks ) {
		$blocks[0]['attrs']['__unstableMaxPages'] = 3;

		return $blocks;
	}

	/**
	 * Generate Styles.
	 *
	 * @return string
	 */
	public function get_responsive_styles( $type = 'default' ) {
		$breaks 	= wecodeart_json( [ 'settings', 'custom', 'breakpoints' ], [] );
		$filter		= wecodeart_json( [ 'settings', 'custom', 'mobileBreakpoint' ], 'lg' );
		$breakpoint	= get_prop( $breaks, $filter, '992px' );

		$inline = '';

		switch( $type ) :
			case 'expand':
				$inline .= "
					.navbar-expand {
						flex-wrap: nowrap;
						justify-content: flex-start;
					}
					.navbar-expand .navbar-nav {
						flex-direction: inherit;
						align-items: inherit;
					}
					.navbar-expand .navbar-nav .dropdown-menu {
						position: absolute;
					}
					.navbar-expand .navbar-nav .nav-link {
						padding-right: 0.5rem;
						padding-left: 0.5rem;
					}
					.navbar-expand .navbar-nav-scroll {
						overflow: visible;
					}
					.navbar-expand .navbar-collapse {
						display: flex !important;
						flex-basis: auto;
					}
					.navbar-expand .navbar-toggler {
						display: none;
					}
				";
			break;
			default:
				foreach( $breaks as $key => $value ) {
					// Skip what we dont need!
					if( $key !== $filter ) continue;

					$inline .= "
					@media (min-width: {$value}) {
						/* Navbar */
						.navbar-expand-{$key} {
							flex-wrap: nowrap;
							justify-content: flex-start;
						}
						.navbar-expand-{$key} .navbar-nav {
							flex-direction: inherit;
							align-items: inherit;
						}
						.navbar-expand-{$key} .navbar-nav .dropdown-menu {
							position: absolute;
						}
						.navbar-expand-{$key} .navbar-nav-scroll {
							overflow: visible;
						}
						.navbar-expand-{$key} .navbar-collapse {
							display: flex !important;
							flex-basis: auto;
						}
						.navbar-expand-{$key} .navbar-toggler {
							display: none;
						}

						/* Block */
						.wp-block-navigation.navbar-expand-{$key} .wp-block-spacer {
							width: var(--wp--spacer-width, 1px);
							vertical-align: -.125em;
							height: 1em;
						}
					}
					";
				}
			break;
		endswitch;
		
		return wecodeart( 'styles' )::compress( $inline );
	}

	/**
	 * Color Shade Styles.
	 *
	 * @return string
	 */
	public function get_color_styles( string $color = 'navbar-light' ) {
		$inline = '';
		
		switch( $color ) :
			case 'navbar-dark':
				$inline .= "
					.navbar-dark {
						--wp--emphasis-color-rgb: var(--wp--color--rgb, 255, 255, 255);
					}
				";
			break;
			case 'navbar-light':
				$inline .= "
					.navbar-light {
						--wp--emphasis-color-rgb: var(--wp--color--rgb, 0, 0, 0);
					}
				";
			break;
		endswitch;

		return wecodeart( 'styles' )::compress( $inline );
	}

	/**
	 * Extra Styles.
	 *
	 * @param	string $variation
	 *
	 * @return 	string
	 */
	public function get_extra_styles( string $variation = 'pills' ): string {
		$inline = '';

		switch( $variation ) :
			case 'pills':
				$inline .= "
					.wp-block-navigation.is-style-pills {
						--wp--nav-pills-border-radius: .375rem;
						--wp--nav-pills-link-active-color: var(--wp--white);
						--wp--nav-pills-link-active-bg: rgb(var(--wp--color--rgb));
						--wp--nav-link-padding-x: 1rem;
    					--wp--nav-link-padding-y: .5rem;
					}
					.wp-block-navigation.is-style-pills .wp-block-navigation-link__dropdown {
						--wp--dropdown-padding-x: .5rem;
					}
					.wp-block-navigation.is-style-pills a {
						background: none;
						border: 0;
						border-radius: var(--wp--nav-pills-border-radius);
					}
					.wp-block-navigation.is-style-pills a.active,
					.wp-block-navigation.is-style-pills .show > a {
						color: var(--wp--nav-pills-link-active-color);
    					background-color: var(--wp--nav-pills-link-active-bg);
					}	
				";
			break;
		endswitch;

		return wecodeart( 'styles' )::compress( $inline );
	}

	/**
	 * Block styles
	 *
	 * @return 	string 	The block styles.
	 */
	public function styles(): string {
		return <<<CSS
			.wp-block-navigation {
				--wp--navigation-flex-direction: column;
				--wp--navigation-flex-wrap: wrap;
				--wp--navigation-justify-content: initial;
				--wp--navigation-align-items: initial;
				--wp--navigation-padding-x: 0;
				--wp--navigation-padding-y: 0.5rem;
				--wp--navigation-color: rgba(var(--wp--emphasis-color-rgb), 1);
				--wp--navigation-hover-color: rgba(var(--wp--emphasis-color-rgb), 0.8);
				--wp--navigation-active-color: rgba(var(--wp--emphasis-color-rgb), 1);
				--wp--navigation-disabled-color: rgba(var(--wp--emphasis-color-rgb), 0.3);
				--wp--nav-link-padding-x: 0;
				--wp--nav-link-padding-y: 0;
				--wp--nav-link-color: var(--wp--navigation-color);
				--wp--nav-link-hover-color: var(--wp--navigation-hover-color);
				--wp--nav-link-disabled-color: var(--wp--navigation-disabled-color);
				position: relative;
			}
			.wp-block-navigation :where(.wp-offcanvas,.wp-offcanvas__body,.wp-block-navigation__container) {
				flex: 1 1 100%;
				gap: inherit;
			}
			/* ul specificity to avoid overwriting editor styles */
			.wp-block-navigation :where(ul.wp-block-navigation__container) {
				display: flex;
				flex-wrap: var(--wp--navigation-flex-wrap, wrap);
				flex-direction: var(--wp--navigation-flex-direction, initial);
				justify-content: var(--wp--navigation-justify-content, initial);
				align-items: var(--wp--navigation-align-items, initial);
				list-style: none;
				margin: 0;
				padding: 0;
			}
			.wp-block-navigation.is-vertical {
				--wp--navigation-flex-direction: column;
				--wp--navigation-justify-content: initial;
				--wp--navigation-align-items: flex-start;
			}
			.wp-block-navigation.is-content-justification-center {
				--wp--navigation-justify-content: center;
			}
			.wp-block-navigation.is-content-justification-center.is-vertical {
				--wp--navigation-flex-align: center;
			}
			.wp-block-navigation.is-content-justification-right {
				--wp--navigation-justify-content: flex-end;
			}
			.wp-block-navigation.is-content-justification-right.is-vertical {
				--wp--navigation-align-items: flex-end;
			}
			.wp-block-navigation.is-content-justification-space-between {
				--wp--navigation-justify-content: space-between;
			}
			.wp-block-navigation.no-wrap {
				--wp--navigation-layout-wrap: nowrap;
			}
			:is(.wp-site-header,.wp-site-footer) .wp-block-navigation {
				padding-top: 0;
				padding-bottom: 0;
			}
			@media (prefers-reduced-motion: reduce) {
				.wp-block-navigation :where(.navbar-toggler,.wp-offcanvas,.wp-block-navigation-item a) {
					transition: none;
				}
			}
		CSS;
	}
}
