<?php
/**
 * Tool for reading existing posts with parsed block structure.
 *
 * @package AgenticWP
 */

namespace Agentic_WP;

defined( 'ABSPATH' ) || exit;

/**
 * Tool for reading existing posts with parsed block structure.
 * Provides block hierarchy and previews for editing workflows.
 */
class Tool_Read_Post {

	/**
	 * Define tool schema for OpenAI Responses API.
	 *
	 * @return array Schema definition
	 */
	public static function schema(): array {
		return array(
			'type'        => 'function',
			'name'        => 'read_post',
			'description' => 'Read existing post content with parsed block structure and previews for editing',
			'strict'      => true,
			'parameters'  => array(
				'type'                 => 'object',
				'properties'           => array(
					'post_identifier'  => array(
						'type'        => 'string',
						'description' => 'Post identifier: numeric ID (e.g. "382"), post slug (e.g. "my-post"), or URL (e.g. "/my-post/" or "http://site.com/my-post/")',
					),
					'include_metadata' => array(
						'type'        => 'boolean',
						'description' => 'Include post metadata (title, status, taxonomies, etc.)',
					),
					'preview_length'   => array(
						'type'        => 'integer',
						'description' => 'Maximum characters for block previews (default: 100)',
					),
				),
				'required'             => array( 'post_identifier', 'include_metadata', 'preview_length' ),
				'additionalProperties' => false,
			),
		);
	}

	/**
	 * Execute the read_post tool.
	 *
	 * @param array $args Tool arguments.
	 * @return string JSON response or error message.
	 */
	public static function run( array $args ): string {
		if ( ! function_exists( 'get_post' ) || ! function_exists( 'parse_blocks' ) ) {
			return 'ERROR: wp_functions_unavailable';
		}

		$post_id = self::resolve_post_identifier( $args['post_identifier'] ?? null );
		if ( is_string( $post_id ) ) {
			return $post_id;
		}

		$include_metadata = isset( $args['include_metadata'] ) ? (bool) $args['include_metadata'] : true;
		$preview_length   = isset( $args['preview_length'] ) && is_int( $args['preview_length'] ) ?
			max( 50, min( 500, $args['preview_length'] ) ) : 100;

		$post = get_post( $post_id );
		if ( ! $post || 'auto-draft' === $post->post_status ) {
			return 'ERROR: post_not_found';
		}

		if ( ! current_user_can( 'edit_post', $post_id ) ) {
			return 'ERROR: insufficient_permissions';
		}

		$blocks           = parse_blocks( $post->post_content );
		$processed_blocks = self::process_blocks_recursive( $blocks, array(), $preview_length );

		$response = array(
			'success'     => true,
			'post_id'     => $post_id,
			'block_count' => count( $blocks ),
			'blocks'      => $processed_blocks,
		);

		if ( $include_metadata ) {
			$response['metadata'] = self::collect_post_metadata( $post );
		}

		return wp_json_encode( $response );
	}

	/**
	 * Parse blocks and generate path-based structure.
	 *
	 * @param array $blocks Parsed blocks array.
	 * @param array $current_path Current path for recursion.
	 * @param int   $preview_length Maximum preview length.
	 * @return array Structured blocks with paths and previews.
	 */
	private static function process_blocks_recursive( array $blocks, array $current_path = array(), int $preview_length = 100 ): array {
		$processed_blocks = array();

		foreach ( $blocks as $index => $block ) {
			$block_path = array_merge( $current_path, array( $index ) );
			$preview    = self::generate_block_preview( $block, $preview_length );

			$processed_block = array(
				'path'      => $block_path,
				'blockName' => $block['blockName'] ?? null,
				'preview'   => $preview,
				'attrs'     => $block['attrs'] ?? array(),
				'innerHTML' => $block['innerHTML'] ?? '',
			);

			if ( ! empty( $block['innerBlocks'] ) ) {
				$processed_block['innerBlocks']    = self::process_blocks_recursive(
					$block['innerBlocks'],
					$block_path,
					$preview_length
				);
				$processed_block['hasInnerBlocks'] = true;
			} else {
				$processed_block['hasInnerBlocks'] = false;
			}

			$processed_blocks[] = $processed_block;
		}

		return $processed_blocks;
	}

	/**
	 * Generate human-readable preview for a block.
	 *
	 * @param array $block Block structure.
	 * @param int   $max_length Maximum preview length.
	 * @return string Preview text.
	 */
	private static function generate_block_preview( array $block, int $max_length ): string {
		$block_name = $block['blockName'] ?? 'Unknown';
		$content    = '';

		if ( ! empty( $block['innerHTML'] ) ) {
			$content = wp_strip_all_tags( $block['innerHTML'] );
		} elseif ( ! empty( $block['attrs'] ) ) {
			if ( isset( $block['attrs']['url'] ) ) {
				$content = 'Image: ' . basename( $block['attrs']['url'] );
			} elseif ( isset( $block['attrs']['title'] ) ) {
				$content = $block['attrs']['title'];
			}
		}

		if ( empty( $content ) ) {
			$content = str_replace( 'core/', '', $block_name ) . ' block';
		}

		$content = trim( preg_replace( '/\s+/', ' ', $content ) );
		if ( strlen( $content ) > $max_length ) {
			$content = substr( $content, 0, $max_length - 3 ) . '...';
		}

		return $content;
	}

	/**
	 * Collect post metadata for editing context.
	 *
	 * @param \WP_Post $post Post object.
	 * @return array Metadata array.
	 */
	private static function collect_post_metadata( \WP_Post $post ): array {
		$metadata = array(
			'ID'            => $post->ID,
			'post_title'    => $post->post_title,
			'post_status'   => $post->post_status,
			'post_type'     => $post->post_type,
			'post_author'   => $post->post_author,
			'post_date'     => $post->post_date,
			'post_modified' => $post->post_modified,
			'post_excerpt'  => $post->post_excerpt,
			'post_parent'   => $post->post_parent,
			'menu_order'    => $post->menu_order,
			'permalink'     => get_permalink( $post->ID ),
			'edit_link'     => get_edit_post_link( $post->ID, '' ),
		);

		$lock_status             = self::get_post_lock_status( $post->ID );
		$metadata['lock_status'] = $lock_status;

		$featured_image_id = get_post_thumbnail_id( $post->ID );
		if ( $featured_image_id ) {
			$metadata['featured_image'] = array(
				'id'  => $featured_image_id,
				'url' => get_the_post_thumbnail_url( $post->ID, 'medium' ),
			);
		}

		if ( 'post' === $post->post_type ) {
			$categories             = get_the_category( $post->ID );
			$metadata['categories'] = array_map(
				function ( $cat ) {
					return array(
						'id'   => $cat->term_id,
						'name' => $cat->name,
						'slug' => $cat->slug,
					);
				},
				$categories
			);

			$tags = get_the_tags( $post->ID );
			if ( $tags ) {
				$metadata['tags'] = array_map(
					function ( $tag ) {
						return array(
							'id'   => $tag->term_id,
							'name' => $tag->name,
							'slug' => $tag->slug,
						);
					},
					$tags
				);
			}
		}

		$taxonomies = get_object_taxonomies( $post->post_type, 'objects' );
		foreach ( $taxonomies as $tax_slug => $taxonomy ) {
			if ( ! $taxonomy->public || in_array( $tax_slug, array( 'category', 'post_tag' ), true ) ) {
				continue;
			}

			$terms = get_the_terms( $post->ID, $tax_slug );
			if ( $terms && ! is_wp_error( $terms ) ) {
				$metadata['taxonomies'][ $tax_slug ] = array_map(
					function ( $term ) {
						return array(
							'id'   => $term->term_id,
							'name' => $term->name,
							'slug' => $term->slug,
						);
					},
					$terms
				);
			}
		}

		return $metadata;
	}


	/**
	 * Get post lock status information.
	 *
	 * @param int $post_id Post ID to check.
	 * @return array Lock status information.
	 */
	private static function get_post_lock_status( int $post_id ): array {
		$lock_status = array(
			'is_locked'           => false,
			'locked_by_user_id'   => null,
			'locked_by_user_name' => null,
			'locked_at'           => null,
		);

		if ( ! function_exists( 'wp_check_post_lock' ) ) {
			return $lock_status;
		}

		$locked_user_id = wp_check_post_lock( $post_id );

		if ( $locked_user_id ) {
			$lock_status['is_locked']         = true;
			$lock_status['locked_by_user_id'] = $locked_user_id;

			$user = get_userdata( $locked_user_id );
			if ( $user ) {
				$lock_status['locked_by_user_name'] = $user->display_name;
			}

			$lock_meta = get_post_meta( $post_id, '_edit_lock', true );
			if ( $lock_meta ) {
				$lock_parts = explode( ':', $lock_meta );
				if ( count( $lock_parts ) === 2 && is_numeric( $lock_parts[0] ) ) {
					$lock_status['locked_at'] = gmdate( 'Y-m-d H:i:s', (int) $lock_parts[0] );
				}
			}
		}

		return $lock_status;
	}

	/**
	 * Resolve post identifier to post ID.
	 *
	 * @param mixed $identifier Post ID, slug, or URL.
	 * @return int|string Post ID on success, error message on failure.
	 */
	private static function resolve_post_identifier( $identifier ) {
		if ( empty( $identifier ) ) {
			return 'ERROR: missing_post_identifier';
		}

		$identifier = trim( (string) $identifier );

		if ( is_numeric( $identifier ) ) {
			$post_id = (int) $identifier;
			return $post_id > 0 ? $post_id : 'ERROR: invalid_post_id';
		}

		if ( strpos( $identifier, 'http' ) === 0 || strpos( $identifier, '/' ) === 0 ) {
			return self::resolve_url_to_post_id( $identifier );
		}

		return self::resolve_slug_to_post_id( $identifier );
	}

	/**
	 * Resolves URL to post ID.
	 *
	 * @since 1.0.0
	 *
	 * @param string $url URL to resolve.
	 * @return int|string Post ID on success, error message on failure.
	 */
	private static function resolve_url_to_post_id( string $url ): mixed {
		$post_id = url_to_postid( $url );

		if ( $post_id > 0 ) {
			return $post_id;
		}

		$parsed_url = wp_parse_url( $url );
		if ( isset( $parsed_url['path'] ) ) {
			$path_parts = explode( '/', trim( $parsed_url['path'], '/' ) );
			$slug       = end( $path_parts );

			if ( ! empty( $slug ) ) {
				return self::resolve_slug_to_post_id( $slug );
			}
		}

		return 'ERROR: invalid_url';
	}

	/**
	 * Resolves slug to post ID.
	 *
	 * @since 1.0.0
	 *
	 * @param string $slug Post slug.
	 * @return int|string Post ID on success, error message on failure.
	 */
	private static function resolve_slug_to_post_id( string $slug ): mixed {
		if ( empty( $slug ) ) {
			return 'ERROR: empty_slug';
		}

		$post_types = get_post_types( array( 'public' => true ), 'names' );

		$query = new \WP_Query(
			array(
				'name'           => $slug,
				'post_type'      => $post_types,
				'post_status'    => array( 'publish', 'draft', 'private' ),
				'posts_per_page' => 1,
				'fields'         => 'ids',
			)
		);

		if ( $query->have_posts() ) {
			return $query->posts[0];
		}

		$page = get_page_by_path( $slug, OBJECT, $post_types );
		if ( $page ) {
			return $page->ID;
		}

		return 'ERROR: slug_not_found';
	}
}
