<?php
/**
 * Shared query + markup helpers for the brentwood/blog block.
 * Used by render.php (initial page render) AND the REST endpoint that powers
 * AJAX search/pagination — one source of truth for the card markup.
 */
if ( ! defined( 'ABSPATH' ) ) { exit; }

/**
 * Normalize a raw config (block attributes or REST params) into safe values.
 */
function bw_blog_sanitize_config( $raw ) {
	$ids = function ( $v ) {
		if ( is_string( $v ) ) {
			$v = explode( ',', $v );
		}
		return array_values( array_filter( array_map( 'intval', (array) $v ) ) );
	};
	$sticky = isset( $raw['sticky'] ) ? (string) $raw['sticky'] : 'default';
	$ratio  = isset( $raw['ratio'] ) ? (string) $raw['ratio'] : '66';
	return array(
		'per_page'   => min( 50, max( 1, (int) ( $raw['per_page'] ?? 8 ) ) ),
		'offset'     => min( 10000, max( 0, (int) ( $raw['offset'] ?? 0 ) ) ),
		'categories' => $ids( $raw['categories'] ?? array() ),
		'tags'       => $ids( $raw['tags'] ?? array() ),
		// Named author_id on the wire: a bare "author" query param is blocked
		// by the bw-request-firewall mu-plugin (user-enumeration protection)
		// and 302s the request to the homepage.
		'author_id'  => max( 0, (int) ( $raw['author_id'] ?? $raw['author'] ?? 0 ) ),
		'sticky'     => in_array( $sticky, array( 'default', 'first', 'exclude', 'only' ), true ) ? $sticky : 'default',
		'ratio'      => in_array( $ratio, array( '56.25', '66', '75', '100' ), true ) ? $ratio : '66',
		// Layout flag: render the newest post as a large "feature" above the
		// grid (page 1 only), mirroring brentwood.ca/news. Carried in the
		// block's data-config so the AJAX endpoint knows to split page 1.
		'featured'   => ( ! empty( $raw['featured'] ) && '0' !== (string) $raw['featured'] ) ? 1 : 0,
	);
}

function bw_blog_base_args( $cfg, $search ) {
	$args = array(
		'post_type'           => 'post',
		'post_status'         => 'publish',
		'ignore_sticky_posts' => true,
		'orderby'             => 'date',
		'order'               => 'DESC',
	);
	if ( $cfg['categories'] ) {
		$args['category__in'] = $cfg['categories'];
	}
	if ( $cfg['tags'] ) {
		$args['tag__in'] = $cfg['tags'];
	}
	if ( $cfg['author_id'] ) {
		$args['author'] = $cfg['author_id'];
	}
	if ( '' !== $search ) {
		$args['s'] = $search;
	}
	return $args;
}

/**
 * Run the configured query for a given page + search term.
 * The block-level offset shifts the whole result stream; pagination starts
 * after it. Sticky modes: default (pure date order), first (sticky posts
 * pinned ahead of the stream), exclude, only.
 *
 * @return array { posts: WP_Post[], total_pages: int, found: int }
 */
function bw_blog_query( $cfg, $page, $search ) {
	$pp         = $cfg['per_page'];
	$page       = max( 1, (int) $page );
	$sticky_ids = array_values( array_filter( array_map( 'intval', (array) get_option( 'sticky_posts', array() ) ) ) );

	if ( 'first' === $cfg['sticky'] && $sticky_ids ) {
		// Sticky posts that also match the filters, newest first.
		$sq       = new WP_Query( array_merge( bw_blog_base_args( $cfg, $search ), array(
			'post__in'       => $sticky_ids,
			'posts_per_page' => -1,
		) ) );
		$stickies = $sq->posts;
		$n        = count( $stickies );
		$start    = $cfg['offset'] + ( $page - 1 ) * $pp;
		$posts    = $start < $n ? array_slice( $stickies, $start - 0, $pp ) : array();
		$need     = $pp - count( $posts );

		$others_args = array_merge( bw_blog_base_args( $cfg, $search ), array(
			'post__not_in' => $sticky_ids,
		) );
		if ( $need > 0 ) {
			$others_args['posts_per_page'] = $need;
			$others_args['offset']         = max( 0, $start - $n );
		} else {
			// Only need the total count.
			$others_args['posts_per_page'] = 1;
			$others_args['fields']         = 'ids';
		}
		$oq = new WP_Query( $others_args );
		if ( $need > 0 ) {
			$posts = array_merge( $posts, $oq->posts );
		}
		$total = max( 0, $n + (int) $oq->found_posts - $cfg['offset'] );
		return array(
			'posts'       => $posts,
			'total_pages' => max( 1, (int) ceil( $total / $pp ) ),
			'found'       => $total,
		);
	}

	$args = array_merge( bw_blog_base_args( $cfg, $search ), array(
		'posts_per_page' => $pp,
		'offset'         => $cfg['offset'] + ( $page - 1 ) * $pp,
	) );
	if ( 'exclude' === $cfg['sticky'] && $sticky_ids ) {
		$args['post__not_in'] = $sticky_ids;
	}
	if ( 'only' === $cfg['sticky'] ) {
		if ( ! $sticky_ids ) {
			return array( 'posts' => array(), 'total_pages' => 1, 'found' => 0 );
		}
		$args['post__in'] = $sticky_ids;
	}
	$q     = new WP_Query( $args );
	$total = max( 0, (int) $q->found_posts - $cfg['offset'] );
	return array(
		'posts'       => $q->posts,
		'total_pages' => max( 1, (int) ceil( $total / $pp ) ),
		'found'       => $total,
	);
}

/**
 * The single newest post used for the fixed feature — always the head of the
 * stream (block offset), independent of which grid page is showing. The grid
 * paginates the rest (offset + 1), so this post never repeats in the grid.
 *
 * @return WP_Post|null
 */
function bw_blog_featured_post( $cfg, $search ) {
	$args = array_merge( bw_blog_base_args( $cfg, $search ), array(
		'posts_per_page' => 1,
		'offset'         => $cfg['offset'],
	) );
	if ( 'exclude' === $cfg['sticky'] ) {
		$sticky_ids = array_values( array_filter( array_map( 'intval', (array) get_option( 'sticky_posts', array() ) ) ) );
		if ( $sticky_ids ) {
			$args['post__not_in'] = $sticky_ids;
		}
	}
	$q = new WP_Query( $args );
	return $q->posts ? $q->posts[0] : null;
}

/** Shared FontAwesome "clock" glyph used in every card's meta row. */
function bw_blog_clock_svg() {
	return '<svg viewBox="0 0 512 512" aria-hidden="true" focusable="false" role="img" '
		. 'xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M464 256A208 208 0 1 1 '
		. '48 256a208 208 0 1 1 416 0zM0 256a256 256 0 1 0 512 0A256 256 0 1 0 0 256zM232 '
		. '120V256c0 8 4 15.5 10.7 20l96 64c11 7.4 25.9 4.4 33.3-6.7s4.4-25.9-6.7-33.3L280 '
		. '243.2V120c0-13.3-10.7-24-24-24s-24 10.7-24 24z"></path></svg>';
}

/** Date + relative-time meta row shared by the grid card and the feature. */
function bw_blog_render_meta( $post ) {
	$date = get_the_date( "M j, 'y", $post ) . ' ' . get_the_time( 'g:ia', $post );
	$ago  = human_time_diff( get_post_time( 'U', true, $post ), time() ) . ' ago';
	return '<div class="bw-blog-card__meta"><span>' . esc_html( $date ) . '</span>'
		. '<span class="bw-blog-card__ago">' . bw_blog_clock_svg() . esc_html( $ago ) . '</span></div>';
}

/**
 * One blog card, matching the brentwood.ca publication thumbnail:
 * ratio-locked image, title strip (min 56px), excerpt, date + relative time.
 */
function bw_blog_render_card( $post, $ratio ) {
	$url     = get_permalink( $post );
	$title   = wptexturize( $post->post_title );
	$thumb   = get_the_post_thumbnail_url( $post, 'medium_large' );
	$excerpt = wp_trim_words( wp_strip_all_tags( get_the_excerpt( $post ) ), 36, '…' );

	return '<a class="bw-blog-card" href="' . esc_url( $url ) . '">'
		. '<div class="bw-blog-card__media" style="padding-bottom:' . esc_attr( $ratio ) . '%">'
		. ( $thumb
			? '<img src="' . esc_url( $thumb ) . '" alt="" loading="lazy">'
			: '<div class="bw-blog-card__noimg" aria-hidden="true"></div>' )
		. '</div>'
		. '<div class="bw-blog-card__title"><h3>' . esc_html( $title ) . '</h3></div>'
		. '<div class="bw-blog-card__excerpt"><p>' . esc_html( $excerpt ) . '</p></div>'
		. bw_blog_render_meta( $post )
		. '</a>';
}

/**
 * The large "feature" card for the newest post — mirrors brentwood.ca/news:
 * a wide full-width image on top, then the title / longer excerpt / meta in a
 * band offset to the right half (stacks full-width on mobile). Shown on page 1
 * only when the block's "Featured first post" option is on.
 */
function bw_blog_render_featured_card( $post, $ratio ) {
	$url     = get_permalink( $post );
	$title   = wptexturize( $post->post_title );
	$thumb   = get_the_post_thumbnail_url( $post, 'large' );
	$excerpt = wp_trim_words( wp_strip_all_tags( get_the_excerpt( $post ) ), 60, '…' );

	return '<a class="bw-blog-card bw-blog-card--featured" href="' . esc_url( $url ) . '">'
		. '<div class="bw-blog-card__media" style="padding-bottom:' . esc_attr( $ratio ) . '%">'
		. ( $thumb
			? '<img src="' . esc_url( $thumb ) . '" alt="" loading="lazy">'
			: '<div class="bw-blog-card__noimg" aria-hidden="true"></div>' )
		. '</div>'
		. '<div class="bw-blog-card__body">'
		. '<div class="bw-blog-card__text">'
		. '<div class="bw-blog-card__title"><h3>' . esc_html( $title ) . '</h3></div>'
		. '<div class="bw-blog-card__excerpt"><p>' . esc_html( $excerpt ) . '</p></div>'
		. bw_blog_render_meta( $post )
		. '</div></div>'
		. '</a>';
}

function bw_blog_render_cards( $posts, $ratio ) {
	if ( ! $posts ) {
		return '<p class="bw-blog__empty">' . esc_html__( 'No posts found.', 'kadence-child' ) . '</p>';
	}
	$out = '';
	foreach ( $posts as $post ) {
		$out .= bw_blog_render_card( $post, $ratio );
	}
	return $out;
}

/**
 * Numbered paginator (window of 5) with prev/next angle buttons — buttons,
 * not links, so clicking never changes the URL or scrolls the page.
 */
function bw_blog_render_paginator( $total_pages, $current ) {
	$total_pages = max( 1, (int) $total_pages );
	$current     = min( $total_pages, max( 1, (int) $current ) );
	if ( $total_pages <= 1 ) {
		return '';
	}
	$left  = '<svg viewBox="0 0 320 512" aria-hidden="true" xmlns="http://www.w3.org/2000/svg">'
		. '<path fill="currentColor" d="M41.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l160 160c12.5 12.5 '
		. '32.8 12.5 45.3 0s12.5-32.8 0-45.3L109.3 256 246.6 118.6c12.5-12.5 12.5-32.8 0-45.3s-32.8'
		. '-12.5-45.3 0l-160 160z"></path></svg>';
	$right = '<svg viewBox="0 0 320 512" aria-hidden="true" xmlns="http://www.w3.org/2000/svg">'
		. '<path fill="currentColor" d="M278.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-160 160c-12.5 '
		. '12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L210.7 256 73.4 118.6c-12.5-12.5-12.5-32.8 0-45.3'
		. 's32.8-12.5 45.3 0l160 160z"></path></svg>';

	$win   = 5;
	$start = max( 1, $current - 2 );
	$end   = min( $total_pages, $start + $win - 1 );
	$start = max( 1, $end - $win + 1 );

	$h = '<button type="button" class="bw-blog__page bw-blog__page--prev" data-page="' . ( $current - 1 ) . '"'
		. ( $current <= 1 ? ' disabled' : '' ) . ' aria-label="' . esc_attr__( 'Previous page', 'kadence-child' ) . '">'
		. $left . '</button>';
	for ( $i = $start; $i <= $end; $i++ ) {
		$h .= '<button type="button" class="bw-blog__page' . ( $i === $current ? ' is-current' : '' ) . '" '
			. 'data-page="' . $i . '" aria-label="' . esc_attr( sprintf( __( 'Page %d', 'kadence-child' ), $i ) ) . '"'
			. ( $i === $current ? ' aria-current="true"' : '' ) . '>' . $i . '</button>';
	}
	$h .= '<button type="button" class="bw-blog__page bw-blog__page--next" data-page="' . ( $current + 1 ) . '"'
		. ( $current >= $total_pages ? ' disabled' : '' ) . ' aria-label="' . esc_attr__( 'Next page', 'kadence-child' ) . '">'
		. $right . '</button>';
	return $h;
}
