<?php
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}
/**
 * Theme Reload: Block Patterns
 *
 * @since Theme Reload 1.0
 */

/**
 * Pattern namespace / slug prefix used when registering theme patterns.
 *
 * Defaults to `neurogenesis-theme`. Override with:
 * `add_filter( 'neurogenesis_styler_include_theme_patterns_slug', fn () => 'custom-slug' );`
 *
 * @return string
 */
function neurogenesis_theme_get_block_pattern_slug(): string {
	/**
	 * Filters the pattern slug prefix used by this theme.
	 *
	 * Neurogenesis Styler reads the same filter to discover which registered
	 * patterns belong to the theme (e.g. `custom-slug/hero-1`).
	 *
	 * @since Theme Reload 1.0
	 *
	 * @param string $slug Pattern namespace / prefix. Default `neurogenesis-theme`.
	 */
	$slug = apply_filters( 'neurogenesis_styler_include_theme_patterns_slug', 'thinko' );

	return is_string( $slug ) ? trim( $slug, '/' ) : 'thinko';
}

/**
 * Block pattern category slug used for full-page presets in Neurogenesis Styler.
 *
 * Empty string disables category-based full pages. Override with:
 * `add_filter( 'neurogenesis_styler_full_page_pattern_category', fn () => 'full-page' );`
 *
 * @return string
 */
function neurogenesis_theme_get_full_page_pattern_category(): string {
	/**
	 * Filters the pattern category slug whose patterns appear under
	 * Neurogenesis Styler → Presets → Pages.
	 *
	 * Theme patterns registered with this category are treated as full-page
	 * layouts. Return an empty string to disable this source.
	 *
	 * @since Theme Reload 1.0
	 *
	 * @param string $category Category slug (e.g. `full-page`). Default `''`.
	 */
	$category = apply_filters( 'neurogenesis_styler_full_page_pattern_category', '' );

	return is_string( $category ) ? sanitize_title( $category ) : '';
}

/**
 * Active import layout slug for full-page / starter content helpers.
 *
 * @return string Layout slug, or empty string.
 */
function neurogenesis_theme_get_full_page_layout_slug(): string {
	if ( ! class_exists( \NeurogenesisTheme\Helpers\ContentLayouts::class ) ) {
		return '';
	}

	$slug = \NeurogenesisTheme\Helpers\ContentLayouts::registered_active_slug();
	if ( ! $slug ) {
		$slug = \NeurogenesisTheme\Helpers\ContentLayouts::imported_layout_slug();
	}
	if ( ! $slug ) {
		$slug = \NeurogenesisTheme\Helpers\ContentLayouts::resolve_active_slug();
	}

	return ( is_string( $slug ) && '' !== $slug ) ? $slug : '';
}

/**
 * Resolve pages.json for the active import layout (no filter).
 *
 * Looks up `assets/content/{layout-slug}/pages.json` using the registered,
 * imported, or sole layout slug.
 *
 * @param string $path Unused; accepted so this can be used as a filter callback.
 * @return string Absolute filesystem path, or empty string when unavailable.
 */
function neurogenesis_theme_resolve_full_page_pages_json_path( $path = '' ): string {
	$slug = neurogenesis_theme_get_full_page_layout_slug();
	if ( '' === $slug || ! class_exists( \NeurogenesisTheme\Helpers\ContentLayouts::class ) ) {
		return '';
	}

	$rel = \NeurogenesisTheme\Helpers\ContentLayouts::content_file_path( $slug, 'pages.json' );
	if ( '' === $rel ) {
		return '';
	}

	$file = get_theme_file_path( $rel );

	return ( is_string( $file ) && is_readable( $file ) ) ? $file : '';
}

/**
 * Absolute path to the active layout's pages.json for full-page presets.
 *
 * Override or disable with:
 * `add_filter( 'neurogenesis_styler_full_page_pages_json_path', fn () => '' );`
 *
 * @return string Absolute filesystem path, or empty string when unavailable.
 */
function neurogenesis_theme_get_full_page_pages_json_path(): string {
	/**
	 * Filters the absolute path to pages.json used as full-page patterns in
	 * Neurogenesis Styler → Presets → Pages.
	 *
	 * Each page row (`title`, `content`, …) is treated as a full-page pattern.
	 * Return an empty string to disable this source. The plugin is responsible
	 * for reading and normalizing the file.
	 *
	 * @since Theme Reload 1.0
	 *
	 * @param string $path Absolute path to pages.json, or `''`.
	 */
	$path = apply_filters( 'neurogenesis_styler_full_page_pages_json_path', '' );

	return is_string( $path ) ? $path : '';
}

/**
 * Resolve public URL for starter layout images (no filter).
 *
 * Maps to `assets/content/{layout-slug}/assets/images` — the files referenced
 * by `[NEURO_THEME_IMAGES_URL]/filename` in pages.json content.
 *
 * @param string $url Unused; accepted so this can be used as a filter callback.
 * @return string Trailing-slash-trimmed URL, or empty string.
 */
function neurogenesis_theme_resolve_full_page_theme_images_url( $url = '' ): string {
	// Prefer shipped theme images (FSE-ready, no import required).
	$shipped = get_theme_file_path( 'assets/images' );
	if ( is_string( $shipped ) && is_dir( $shipped ) ) {
		return untrailingslashit( get_theme_file_uri( 'assets/images' ) );
	}

	$slug = neurogenesis_theme_get_full_page_layout_slug();
	if ( '' === $slug || ! class_exists( \NeurogenesisTheme\Helpers\ContentLayouts::class ) ) {
		return '';
	}

	$rel = \NeurogenesisTheme\Helpers\ContentLayouts::content_file_path( $slug, 'assets/images' );
	if ( '' === $rel ) {
		return '';
	}

	$dir = get_theme_file_path( $rel );
	if ( ! is_string( $dir ) || ! is_dir( $dir ) ) {
		return '';
	}

	$uri = \NeurogenesisTheme\Helpers\ContentLayouts::content_file_uri( $slug, 'assets/images' );

	return is_string( $uri ) && '' !== $uri ? untrailingslashit( $uri ) : '';
}

/**
 * Public URL for starter layout images used in full-page `pages.json` content.
 *
 * @return string Trailing-slash-trimmed URL, or empty string.
 */
function neurogenesis_theme_get_full_page_theme_images_url(): string {
	/**
	 * Filters the base URL for `[NEURO_THEME_IMAGES_URL]` placeholders in
	 * full-page pages.json content.
	 *
	 * Plugin should replace `[NEURO_THEME_IMAGES_URL]` with this value (no
	 * trailing slash) so image `src` attributes resolve to theme files under
	 * `assets/content/{layout}/assets/images/`.
	 *
	 * @since Theme Reload 1.0
	 *
	 * @param string $url Base images URL, or `''`.
	 */
	$url = apply_filters( 'neurogenesis_styler_full_page_theme_images_url', '' );

	return is_string( $url ) ? untrailingslashit( $url ) : '';
}

/**
 * Expand `[NEURO_THEME_IMAGES_URL]/…` placeholders to theme asset URLs.
 *
 * Use when turning pages.json / patterns.json rows into editor patterns without
 * importing images into the media library.
 *
 * @param string $content Block markup that may contain image placeholders.
 * @return string
 */
function neurogenesis_theme_expand_full_page_image_placeholders( string $content ): string {
	if ( '' === $content || false === strpos( $content, '[NEURO_THEME_IMAGES_URL]' ) ) {
		return $content;
	}

	$base = neurogenesis_theme_get_full_page_theme_images_url();
	if ( '' === $base ) {
		return $content;
	}

	return str_replace( '[NEURO_THEME_IMAGES_URL]', $base, $content );
}

/**
 * Resolve patterns.json for the active import layout.
 *
 * Looks up `assets/content/{layout-slug}/patterns.json` using the registered,
 * imported, or sole layout slug.
 *
 * @return string Absolute filesystem path, or empty string when unavailable.
 */
function neurogenesis_theme_resolve_patterns_json_path(): string {
	$slug = neurogenesis_theme_get_full_page_layout_slug();
	if ( '' === $slug || ! class_exists( \NeurogenesisTheme\Helpers\ContentLayouts::class ) ) {
		return '';
	}

	$rel = \NeurogenesisTheme\Helpers\ContentLayouts::content_file_path( $slug, 'patterns.json' );
	if ( '' === $rel ) {
		return '';
	}

	$file = get_theme_file_path( $rel );

	return ( is_string( $file ) && is_readable( $file ) ) ? $file : '';
}

/**
 * Resolve a path under the active layout content directory.
 *
 * Relative paths in patterns.json are relative to
 * `assets/content/{layout-slug}/`. Rejects traversal outside that directory.
 *
 * @param string $relative Relative path from the layout content root.
 * @return array{path: string, uri: string}|null Absolute filesystem path + public URI, or null.
 */
function neurogenesis_theme_resolve_layout_content_file( string $relative ): ?array {
	$relative = str_replace( '\\', '/', trim( $relative ) );
	if ( '' === $relative || 0 === strpos( $relative, '/' ) || preg_match( '#^[a-zA-Z]:/#', $relative ) ) {
		return null;
	}

	$segments = array_values(
		array_filter(
			explode( '/', $relative ),
			static function ( $segment ) {
				return '' !== $segment && '.' !== $segment;
			}
		)
	);

	foreach ( $segments as $segment ) {
		if ( '..' === $segment ) {
			return null;
		}
	}

	$relative = implode( '/', $segments );
	if ( '' === $relative ) {
		return null;
	}

	$layout_slug = neurogenesis_theme_get_full_page_layout_slug();
	if ( '' === $layout_slug || ! class_exists( \NeurogenesisTheme\Helpers\ContentLayouts::class ) ) {
		return null;
	}

	$rel = \NeurogenesisTheme\Helpers\ContentLayouts::content_file_path( $layout_slug, $relative );
	if ( '' === $rel ) {
		return null;
	}

	$path = get_theme_file_path( $rel );
	if ( ! is_string( $path ) || ! is_readable( $path ) ) {
		return null;
	}

	$uri = \NeurogenesisTheme\Helpers\ContentLayouts::content_file_uri( $layout_slug, $relative );

	return array(
		'path' => $path,
		'uri'  => is_string( $uri ) ? $uri : '',
	);
}

/**
 * Load block pattern definitions from the active layout's patterns.json.
 *
 * Export shape (array of objects):
 * - `id` (required) — pattern slug key
 * - `markup` (required) — relative path to HTML, e.g. `patterns/{id}.html`
 * - `title`, `type`, `category`, `keywords`, `isPro`, `thumbnail`
 *
 * Markup is read from the external HTML file. `[NEURO_THEME_IMAGES_URL]` in
 * content is expanded to the layout images URL. Missing thumbnail files are
 * ignored (pattern still registers).
 *
 * @return array<string, array<string, mixed>> Pattern definitions keyed by id.
 */
function neurogenesis_theme_load_patterns_from_json(): array {
	$file = neurogenesis_theme_resolve_patterns_json_path();
	if ( '' === $file ) {
		return array();
	}

	$raw = file_get_contents( $file );
	if ( false === $raw || '' === trim( $raw ) ) {
		return array();
	}

	$data = json_decode( $raw, true );
	if ( ! is_array( $data ) ) {
		return array();
	}

	// Prefer a top-level array; also accept `{ "patterns": [ ... ] }`.
	$rows = ( isset( $data['patterns'] ) && is_array( $data['patterns'] ) )
		? $data['patterns']
		: $data;

	$patterns = array();

	foreach ( $rows as $row ) {
		if ( ! is_array( $row ) ) {
			continue;
		}

		$id = isset( $row['id'] ) && is_string( $row['id'] ) ? sanitize_title( $row['id'] ) : '';
		if ( '' === $id ) {
			continue;
		}

		$markup_rel = isset( $row['markup'] ) && is_string( $row['markup'] ) ? trim( $row['markup'] ) : '';
		if ( '' === $markup_rel ) {
			continue;
		}

		$markup_file = neurogenesis_theme_resolve_layout_content_file( $markup_rel );
		if ( null === $markup_file ) {
			continue;
		}

		$content = file_get_contents( $markup_file['path'] );
		if ( false === $content || '' === trim( $content ) ) {
			continue;
		}

		$title = isset( $row['title'] ) && is_string( $row['title'] ) && '' !== trim( $row['title'] )
			? $row['title']
			: $id;

		$definition = array(
			'title'   => $title,
			'content' => neurogenesis_theme_expand_full_page_image_placeholders( $content ),
		);

		$category = isset( $row['category'] ) && is_string( $row['category'] )
			? sanitize_title( $row['category'] )
			: '';
		if ( '' !== $category ) {
			$definition['categories'] = array( $category );
		}

		if ( isset( $row['keywords'] ) && is_array( $row['keywords'] ) ) {
			$definition['keywords'] = array_values(
				array_filter(
					array_map(
						static function ( $keyword ) {
							return is_string( $keyword ) ? sanitize_text_field( $keyword ) : '';
						},
						$row['keywords']
					)
				)
			);
		}

		if ( array_key_exists( 'isPro', $row ) ) {
			$definition['isPro'] = (bool) $row['isPro'];
		}

		$thumbnail_rel = isset( $row['thumbnail'] ) && is_string( $row['thumbnail'] )
			? trim( $row['thumbnail'] )
			: '';
		if ( '' !== $thumbnail_rel ) {
			$thumbnail_file = neurogenesis_theme_resolve_layout_content_file( $thumbnail_rel );
			if ( null !== $thumbnail_file && '' !== $thumbnail_file['uri'] ) {
				$definition['thumbnail'] = $thumbnail_file['uri'];
			}
		}

		// Ignore unknown export fields; keep a few WordPress-native optionals if present.
		foreach ( array( 'description', 'viewportWidth', 'blockTypes', 'inserter' ) as $optional ) {
			if ( array_key_exists( $optional, $row ) ) {
				$definition[ $optional ] = $row[ $optional ];
			}
		}

		$patterns[ $id ] = $definition;
	}

	return $patterns;
}

/**
 * Registers block pattern categories (and optional filtered definitions).
 *
 * Shipped patterns live in `/patterns` and are registered by WordPress core.
 *
 * @since Theme Reload 1.0
 */
function neurogenesis_theme_register_block_patterns() {
	/**
	 * Filters the theme block pattern categories.
	 *
	 * Section/page patterns live in `/patterns` (native FSE registration).
	 * This hook only registers categories so those files resolve cleanly.
	 *
	 * @since Theme Reload 1.0
	 *
	 * @param array<string, array{label: string}> $block_pattern_categories Categories keyed by slug.
	 */
	$block_pattern_categories = apply_filters(
		'neurogenesis_theme_block_pattern_categories',
		array(
			'about'          => array( 'label' => __( 'About', 'thinko' ) ),
			'services'       => array( 'label' => __( 'Services', 'thinko' ) ),
			'call-to-action' => array( 'label' => __( 'Call to Action', 'thinko' ) ),
			'testimonials'   => array( 'label' => __( 'Testimonials', 'thinko' ) ),
			'contact'        => array( 'label' => __( 'Contact', 'thinko' ) ),
			'page'           => array(
				'label'       => __( 'Pages', 'thinko' ),
				'description' => __( 'A collection of full page layouts.', 'thinko' ),
			),
			'query'          => array( 'label' => __( 'Query', 'thinko' ) ),
		)
	);

	if ( is_array( $block_pattern_categories ) ) {
		foreach ( $block_pattern_categories as $name => $properties ) {
			register_block_pattern_category( $name, $properties );
		}
	}

	/**
	 * Optional programmatic pattern definitions (slug => definition).
	 *
	 * Native theme patterns in `/patterns` are registered by WordPress.
	 * Import-layout JSON patterns are no longer auto-registered here to avoid
	 * duplicates with shipped FSE patterns.
	 *
	 * @since Theme Reload 1.0
	 *
	 * @param array<string, array<string, mixed>> $block_patterns Definitions keyed by id.
	 */
	$block_patterns = apply_filters( 'neurogenesis_theme_block_patterns', array() );

	$namespace = neurogenesis_theme_get_block_pattern_slug();

	if ( ! is_array( $block_patterns ) || array() === $block_patterns ) {
		return;
	}

	// Register categories referenced by filtered patterns (when not already registered).
	if ( class_exists( '\WP_Block_Pattern_Categories_Registry' ) ) {
		$category_registry = \WP_Block_Pattern_Categories_Registry::get_instance();
		foreach ( $block_patterns as $definition ) {
			if ( ! is_array( $definition ) || empty( $definition['categories'] ) || ! is_array( $definition['categories'] ) ) {
				continue;
			}
			foreach ( $definition['categories'] as $category_slug ) {
				if ( ! is_string( $category_slug ) || '' === $category_slug ) {
					continue;
				}
				if ( $category_registry->is_registered( $category_slug ) ) {
					continue;
				}
				register_block_pattern_category(
					$category_slug,
					array(
						'label' => ucwords( str_replace( array( '-', '_' ), ' ', $category_slug ) ),
					)
				);
			}
		}
	}

	foreach ( $block_patterns as $block_pattern => $definition ) {
		if ( ! is_string( $block_pattern ) || '' === $block_pattern || ! is_array( $definition ) ) {
			continue;
		}

		if ( empty( $definition['title'] ) || empty( $definition['content'] ) || ! is_string( $definition['content'] ) ) {
			continue;
		}

		$definition['content'] = neurogenesis_theme_expand_full_page_image_placeholders( $definition['content'] );

		register_block_pattern(
			$namespace . '/' . $block_pattern,
			$definition
		);
	}
}
add_action( 'init', 'neurogenesis_theme_register_block_patterns', 9 );

// Include theme patterns in Neurogenesis Styler.
add_filter( 'neurogenesis_styler_include_theme_patterns', '__return_true' );

// Pattern slug prefix (must match how patterns are registered, e.g. thinko/hidden-search).
add_filter( 'neurogenesis_styler_include_theme_patterns_slug', static function () {
	return 'thinko';
} );

// Full-page presets: prefer shipped `page` category patterns; keep pages.json
// as an optional fallback while import layouts still exist.
add_filter(
	'neurogenesis_styler_full_page_pattern_category',
	static function () {
		return 'page';
	}
);
add_filter(
	'neurogenesis_styler_full_page_pages_json_path',
	'neurogenesis_theme_resolve_full_page_pages_json_path'
);
add_filter(
	'neurogenesis_styler_full_page_theme_images_url',
	'neurogenesis_theme_resolve_full_page_theme_images_url'
);
