<?php
/**
 * llms.txt and llms-full.txt endpoints.
 *
 * llms.txt: Structured index of site content for LLM consumption.
 * llms-full.txt: Full clean text of all pages in one file.
 *
 * @package Dennis_SEO
 */

defined( 'ABSPATH' ) || exit;

class Dennis_SEO_Llms_Txt {

	public function __construct() {
		add_action( 'init', array( __CLASS__, 'register_rewrite_rules' ) );
		add_action( 'template_redirect', array( $this, 'serve' ) );
		add_action( 'save_post', array( $this, 'bust_cache' ) );
		add_action( 'delete_post', array( $this, 'bust_cache' ) );
	}

	public static function register_rewrite_rules(): void {
		add_rewrite_rule( '^llms\.txt$', 'index.php?dennis_llms_txt=index', 'top' );
		add_rewrite_rule( '^llms-full\.txt$', 'index.php?dennis_llms_txt=full', 'top' );
		add_filter( 'query_vars', function ( $vars ) {
			$vars[] = 'dennis_llms_txt';
			return $vars;
		} );
	}

	public function serve(): void {
		$type = get_query_var( 'dennis_llms_txt' );
		if ( ! $type ) {
			return;
		}

		if ( ! Dennis_SEO_Settings::get( 'llms_enabled', true ) ) {
			status_header( 404 );
			exit;
		}

		status_header( 200 );
		header( 'Content-Type: text/plain; charset=UTF-8' );
		// Short browser cache so edits are visible quickly. Server-side transient handles the heavy caching.
		header( 'Cache-Control: public, max-age=60' );

		if ( 'full' === $type ) {
			// Serve the content from the settings field. If empty, 404.
			$content = Dennis_SEO_Settings::get( 'llms_full_content', '' );
			if ( empty( trim( $content ) ) ) {
				status_header( 404 );
				exit;
			}
			$output = $content;
		} else {
			$cache_key = 'dennis_seo_llms_txt';
			$cached    = get_transient( $cache_key );
			if ( false !== $cached ) {
				echo $cached; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
				exit;
			}
			$output = $this->generate_index();
			set_transient( $cache_key, $output, HOUR_IN_SECONDS );
		}

		echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
		exit;
	}

	/**
	 * Generate the llms.txt index (page titles, URLs, descriptions).
	 */
	private function generate_index(): string {
		$site_name = Dennis_SEO_Settings::get( 'site_name', get_bloginfo( 'name' ) );
		$site_desc = Dennis_SEO_Settings::get( 'llms_header', Dennis_SEO_Settings::get( 'business_description', get_bloginfo( 'description' ) ) );

		$txt  = "# {$site_name}\n\n";
		if ( ! empty( $site_desc ) ) {
			$txt .= "> {$site_desc}\n\n";
		}

		// Link to full version if it has content.
		$full_content = Dennis_SEO_Settings::get( 'llms_full_content', '' );
		if ( ! empty( trim( $full_content ) ) ) {
			$txt .= "> Full content available at: " . home_url( '/llms-full.txt' ) . "\n\n";
		}

		$default_inclusion = Dennis_SEO_Settings::get( 'llms_default', 'all' );

		// Pages — always included based on default_inclusion setting.
		$pages = $this->get_included_posts( 'page', $default_inclusion );
		if ( ! empty( $pages ) ) {
			$txt .= "## Pages\n\n";
			foreach ( $pages as $page ) {
				$desc = $this->get_description( $page );
				$txt .= "- [{$page->post_title}](" . get_permalink( $page ) . "): {$desc}\n";
			}
			$txt .= "\n";
		}

		// Posts.
		if ( 'all' === $default_inclusion ) {
			$posts = $this->get_included_posts( 'post', $default_inclusion );
			if ( ! empty( $posts ) ) {
				$txt .= "## Posts\n\n";
				foreach ( $posts as $post ) {
					$desc = $this->get_description( $post );
					$txt .= "- [{$post->post_title}](" . get_permalink( $post ) . "): {$desc}\n";
				}
				$txt .= "\n";
			}
		}

		// All other public post types (WooCommerce products, custom post types, etc.)
		$custom_types = get_post_types( array( 'public' => true, '_builtin' => false ), 'objects' );
		foreach ( $custom_types as $pt ) {
			$pt_posts = $this->get_included_posts( $pt->name, 'all' );
			if ( empty( $pt_posts ) ) {
				continue;
			}

			// Get the primary taxonomy for this post type (for grouping).
			$taxonomies = get_object_taxonomies( $pt->name, 'objects' );
			$group_tax  = null;
			foreach ( $taxonomies as $tax ) {
				if ( $tax->hierarchical && $tax->public ) {
					$group_tax = $tax->name;
					break;
				}
			}

			if ( $group_tax ) {
				// Group by taxonomy (e.g., products by product_cat, husmodeller by category).
				$categorized   = array();
				$uncategorized = array();
				foreach ( $pt_posts as $cpt_post ) {
					$terms = wp_get_post_terms( $cpt_post->ID, $group_tax, array( 'fields' => 'names' ) );
					if ( ! empty( $terms ) && ! is_wp_error( $terms ) ) {
						$cat = $terms[0];
						if ( ! isset( $categorized[ $cat ] ) ) {
							$categorized[ $cat ] = array();
						}
						$categorized[ $cat ][] = $cpt_post;
					} else {
						$uncategorized[] = $cpt_post;
					}
				}

				ksort( $categorized );
				foreach ( $categorized as $cat_name => $cat_posts ) {
					$txt .= "## {$pt->labels->name} — {$cat_name}\n\n";
					foreach ( $cat_posts as $cpt_post ) {
						$desc = $this->get_description( $cpt_post );
						$txt .= "- [{$cpt_post->post_title}](" . get_permalink( $cpt_post ) . "): {$desc}\n";
					}
					$txt .= "\n";
				}

				if ( ! empty( $uncategorized ) ) {
					$txt .= "## {$pt->labels->name}\n\n";
					foreach ( $uncategorized as $cpt_post ) {
						$desc = $this->get_description( $cpt_post );
						$txt .= "- [{$cpt_post->post_title}](" . get_permalink( $cpt_post ) . "): {$desc}\n";
					}
					$txt .= "\n";
				}
			} else {
				// No grouping taxonomy — list flat.
				$txt .= "## {$pt->labels->name}\n\n";
				foreach ( $pt_posts as $cpt_post ) {
					$desc = $this->get_description( $cpt_post );
					$txt .= "- [{$cpt_post->post_title}](" . get_permalink( $cpt_post ) . "): {$desc}\n";
				}
				$txt .= "\n";
			}
		}

		return $txt;
	}

	/**
	 * Get posts included in llms.txt.
	 */
	private function get_included_posts( string $post_type, string $default_inclusion ): array {
		$args = array(
			'post_type'      => $post_type,
			'post_status'    => 'publish',
			'posts_per_page' => 500,
			'orderby'        => 'menu_order title',
			'order'          => 'ASC',
		);

		if ( 'none' === $default_inclusion ) {
			$args['meta_query'] = array(
				array(
					'key'   => '_dennis_seo_llms_include',
					'value' => 'yes',
				),
			);
		} elseif ( 'pages' === $default_inclusion && 'post' === $post_type ) {
			$args['meta_query'] = array(
				array(
					'key'   => '_dennis_seo_llms_include',
					'value' => 'yes',
				),
			);
		}

		$posts = get_posts( $args );

		return array_filter( $posts, function ( $post ) {
			$include = get_post_meta( $post->ID, '_dennis_seo_llms_include', true );
			return 'no' !== $include;
		} );
	}

	/**
	 * Get short description for llms.txt index.
	 */
	private function get_description( $post ): string {
		$desc = get_post_meta( $post->ID, '_dennis_seo_llms_description', true );
		if ( ! empty( $desc ) ) {
			return $desc;
		}

		$desc = Dennis_SEO_Meta_Box::get_meta( $post->ID, '_dennis_seo_description' );
		if ( ! empty( $desc ) ) {
			return wp_trim_words( $desc, 25, '...' );
		}

		$text = ! empty( $post->post_excerpt ) ? $post->post_excerpt : $post->post_content;
		return wp_trim_words( wp_strip_all_tags( $text ), 20, '...' );
	}

	public function bust_cache(): void {
		delete_transient( 'dennis_seo_llms_txt' );
		delete_transient( 'dennis_seo_llms_full_txt' );
	}
}
