<?php
/**
 * Plugin Name:       BW Pretty Post Link
 * Plugin URI:        https://bowdenworks.com
 * Description:       Gutenberg block for outputting a list of nicely-rendered post links. Each item can be an Internal post (pick any public post type) or an External link. Two layouts: Simple (plain list) and Thumbnail (16:9 thumb + title).
 * Version:           1.0.0
 * Requires at least: 6.3
 * Requires PHP:      7.4
 * Author:            Bowden Works
 * Author URI:        https://bowdenworks.com
 * License:           GPL-2.0-or-later
 * License URI:       https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:       bw-pretty-post-link
 *
 * @package BW_Pretty_Post_Link
 */

defined( 'ABSPATH' ) || exit;

define( 'BW_PPL_VERSION', '1.0.0' );
define( 'BW_PPL_DIR', plugin_dir_path( __FILE__ ) );
define( 'BW_PPL_URL', plugin_dir_url( __FILE__ ) );

/**
 * Register the two blocks (list + item) and their server render callbacks.
 */
function bw_ppl_register_blocks() {

	// Register item block (child).
	register_block_type(
		'bw/pretty-post-link-item',
		array(
			'api_version'     => 3,
			'title'           => __( 'Pretty Post Link Item', 'bw-pretty-post-link' ),
			'description'     => __( 'A single post link — internal (from any post type) or external.', 'bw-pretty-post-link' ),
			'category'        => 'widgets',
			'icon'            => 'admin-links',
			'parent'          => array( 'bw/pretty-post-link-list' ),
			'keywords'        => array( 'link', 'post', 'thumbnail' ),
			'supports'        => array(
				'html'              => false,
				'reusable'          => false,
				'__experimentalToolbar' => false,
			),
			'attributes'      => array(
				'linkType'             => array( 'type' => 'string', 'default' => 'internal' ),
				'postType'             => array( 'type' => 'string', 'default' => 'post' ),
				'postId'               => array( 'type' => 'number', 'default' => 0 ),
				'customTitle'          => array( 'type' => 'string', 'default' => '' ),
				'customThumbnailId'    => array( 'type' => 'number', 'default' => 0 ),
				'externalTitle'        => array( 'type' => 'string', 'default' => '' ),
				'externalUrl'          => array( 'type' => 'string', 'default' => '' ),
				'externalThumbnailId'  => array( 'type' => 'number', 'default' => 0 ),
			),
			'render_callback' => 'bw_ppl_render_item',
		)
	);

	// Register list block (parent).
	register_block_type(
		'bw/pretty-post-link-list',
		array(
			'api_version'     => 3,
			'title'           => __( 'Pretty Post Link List', 'bw-pretty-post-link' ),
			'description'     => __( 'A list of nicely-rendered post links — internal or external — in Simple or Thumbnail layout.', 'bw-pretty-post-link' ),
			'category'        => 'widgets',
			'icon'            => 'list-view',
			'keywords'        => array( 'link', 'list', 'post', 'thumbnail', 'pretty' ),
			'supports'        => array(
				'html'  => false,
				'align' => array( 'wide', 'full' ),
				'anchor' => true,
			),
			'attributes'      => array(
				'layout'          => array( 'type' => 'string', 'default' => 'simple' ),
				'imgWidthPercent' => array( 'type' => 'number', 'default' => 20 ),
			),
			'render_callback' => 'bw_ppl_render_list',
		)
	);
}
add_action( 'init', 'bw_ppl_register_blocks' );

/**
 * Load plugin text domain for translations.
 */
function bw_ppl_load_textdomain() {
	load_plugin_textdomain( 'bw-pretty-post-link', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
}
add_action( 'plugins_loaded', 'bw_ppl_load_textdomain' );


/**
 * Enqueue editor script & styles (both blocks share one bundle for simplicity).
 */
function bw_ppl_enqueue_editor_assets() {
	wp_enqueue_script(
		'bw-ppl-editor',
		BW_PPL_URL . 'assets/editor.js',
		array(
			'wp-blocks',
			'wp-element',
			'wp-block-editor',
			'wp-components',
			'wp-data',
			'wp-api-fetch',
			'wp-i18n',
		),
		BW_PPL_VERSION,
		true
	);

	wp_enqueue_style(
		'bw-ppl-editor',
		BW_PPL_URL . 'assets/editor.css',
		array(),
		BW_PPL_VERSION
	);

	// The frontend styles are also loaded in the editor so blocks preview correctly.
	wp_enqueue_style(
		'bw-ppl-frontend',
		BW_PPL_URL . 'assets/frontend.css',
		array(),
		BW_PPL_VERSION
	);
}
add_action( 'enqueue_block_editor_assets', 'bw_ppl_enqueue_editor_assets' );


/**
 * Register frontend style (loaded on-demand by the list block's render callback).
 */
function bw_ppl_register_frontend_style() {
	wp_register_style(
		'bw-ppl-frontend',
		BW_PPL_URL . 'assets/frontend.css',
		array(),
		BW_PPL_VERSION
	);
}
add_action( 'init', 'bw_ppl_register_frontend_style' );


/**
 * Render callback: the parent list block.
 *
 * @param array  $attributes Block attributes.
 * @param string $content    Inner blocks HTML (the rendered items).
 * @return string
 */
function bw_ppl_render_list( $attributes, $content ) {
	wp_enqueue_style( 'bw-ppl-frontend' );

	$layout    = ! empty( $attributes['layout'] ) ? $attributes['layout'] : 'simple';
	$img_width = isset( $attributes['imgWidthPercent'] ) ? intval( $attributes['imgWidthPercent'] ) : 20;
	$img_width = max( 10, min( 60, $img_width ) );

	if ( ! in_array( $layout, array( 'simple', 'thumbnail' ), true ) ) {
		$layout = 'simple';
	}

	$content = trim( $content );

	$wrapper_attrs = get_block_wrapper_attributes(
		array(
			'class' => 'bw-ppl bw-ppl--' . $layout,
			'style' => '--bw-ppl-img-width: ' . $img_width . '%;',
		)
	);

	return sprintf( '<ul %1$s>%2$s</ul>', $wrapper_attrs, $content );
}


/**
 * Render callback: a single item.
 *
 * @param array $attributes Block attributes.
 * @return string
 */
function bw_ppl_render_item( $attributes ) {

	$type = ! empty( $attributes['linkType'] ) ? $attributes['linkType'] : 'internal';

	$title    = '';
	$url      = '';
	$thumb_id = 0;
	$target   = '';
	$rel      = '';

	if ( 'external' === $type ) {

		$title    = isset( $attributes['externalTitle'] ) ? trim( $attributes['externalTitle'] ) : '';
		$url      = isset( $attributes['externalUrl'] ) ? esc_url_raw( $attributes['externalUrl'] ) : '';
		$thumb_id = isset( $attributes['externalThumbnailId'] ) ? intval( $attributes['externalThumbnailId'] ) : 0;
		$target   = ' target="_blank"';
		$rel      = ' rel="noopener noreferrer"';

	} else {

		$post_id = isset( $attributes['postId'] ) ? intval( $attributes['postId'] ) : 0;
		if ( ! $post_id ) {
			return '';
		}
		$post = get_post( $post_id );
		if ( ! $post || 'publish' !== $post->post_status ) {
			return '';
		}

		$custom_title = isset( $attributes['customTitle'] ) ? trim( $attributes['customTitle'] ) : '';
		$title        = $custom_title !== '' ? $custom_title : get_the_title( $post );

		$url = get_permalink( $post );

		$custom_thumb_id = isset( $attributes['customThumbnailId'] ) ? intval( $attributes['customThumbnailId'] ) : 0;
		$thumb_id        = $custom_thumb_id ? $custom_thumb_id : get_post_thumbnail_id( $post );
	}

	if ( $title === '' || $url === '' ) {
		return '';
	}

	if ( $thumb_id ) {
		$thumb_html = wp_get_attachment_image(
			$thumb_id,
			'medium_large',
			false,
			array(
				'class'   => 'bw-ppl__thumb',
				'loading' => 'lazy',
				'alt'     => '',
			)
		);
	} else {
		$thumb_html = '<span class="bw-ppl__thumb bw-ppl__thumb--placeholder" aria-hidden="true"></span>';
	}

	$html  = '<li class="wp-block-bw-pretty-post-link-item bw-ppl__item">';
	$html .= '<a class="bw-ppl__link" href="' . esc_url( $url ) . '"' . $target . $rel . '>';
	$html .= '<span class="bw-ppl__thumb-wrap">' . $thumb_html . '</span>';
	$html .= '<span class="bw-ppl__title">' . esc_html( $title ) . '</span>';
	$html .= '</a>';
	$html .= '</li>';

	return $html;
}


/**
 * REST endpoint: return public post types (excluding attachments & blocks-related types).
 */
function bw_ppl_register_rest_routes() {
	register_rest_route(
		'bw-ppl/v1',
		'/post-types',
		array(
			'methods'             => 'GET',
			'callback'            => 'bw_ppl_rest_post_types',
			'permission_callback' => function () {
				return current_user_can( 'edit_posts' );
			},
		)
	);
}
add_action( 'rest_api_init', 'bw_ppl_register_rest_routes' );

function bw_ppl_rest_post_types( $request ) {
	$excluded = array( 'attachment', 'wp_block', 'wp_template', 'wp_template_part', 'wp_navigation' );
	$types    = get_post_types( array( 'public' => true ), 'objects' );
	$out      = array();
	foreach ( $types as $slug => $obj ) {
		if ( in_array( $slug, $excluded, true ) ) {
			continue;
		}
		$out[] = array(
			'slug'      => $slug,
			'label'     => $obj->labels && ! empty( $obj->labels->singular_name ) ? $obj->labels->singular_name : $obj->label,
			'rest_base' => ! empty( $obj->rest_base ) ? $obj->rest_base : $slug,
		);
	}
	return $out;
}
