<?php
/**
 * Top Fundraisers Block
 *
 * Displays the leading fundraisers for the current root fundraiser (or a preset
 * root) ranked by amount raised. Each entry links directly to the fundraiser page
 * so donors can immediately support a specific ambassador.
 *
 * @package AmbassadorHub\Fundraisers\Blocks\TopFundraisers
 */

namespace AmbassadorHub\Fundraisers\Blocks\TopFundraisers;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared

use AmbassadorHub\Fundraisers\Admin\Settings\SettingsManager;
use AmbassadorHub\Fundraisers\FundraisersHelpers;
use AmbassadorHub\Fundraisers\FundraisersModule;

/**
 * Top Fundraisers block registration and rendering.
 */
final class Block {

	// -------------------------------------------------------------------------
	// Boot
	// -------------------------------------------------------------------------

	public static function init(): void {
		add_action( 'init', array( self::class, 'register_block' ) );
	}

	// -------------------------------------------------------------------------
	// Registration
	// -------------------------------------------------------------------------

	public static function register_block(): void {
		$block_dir = AMBASSADOR_HUB_PATH . 'src/Fundraisers/Blocks/TopFundraisers/';
		$block_url = AMBASSADOR_HUB_URL . 'src/Fundraisers/Blocks/TopFundraisers/';

		$script_file = $block_dir . 'index.js';
		if ( file_exists( $script_file ) ) {
			wp_register_script(
				'ambh-top-fundraisers-block',
				$block_url . 'index.js',
				array( 'wp-blocks', 'wp-i18n', 'wp-element', 'wp-block-editor', 'wp-components', 'wp-server-side-render', 'wp-api-fetch' ),
				(string) filemtime( $script_file ),
				true
			);
		}

		$style_file = $block_dir . 'style.css';
		wp_register_style(
			'ambh-top-fundraisers',
			$block_url . 'style.css',
			array(),
			file_exists( $style_file ) ? (string) filemtime( $style_file ) : AMBASSADOR_HUB_VERSION
		);

		$block_args = array(
			'style'           => 'ambh-top-fundraisers',
			'render_callback' => array( self::class, 'render_block' ),
		);
		if ( file_exists( $script_file ) ) {
			$block_args['editor_script'] = 'ambh-top-fundraisers-block';
		}

		register_block_type( $block_dir, $block_args );
	}

	// -------------------------------------------------------------------------
	// Rendering
	// -------------------------------------------------------------------------

	public static function render_block( array $attributes = array() ): string {

		if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
			return '<div class="ambh-top-fundraisers ambh-block-preview">'
				. esc_html__( 'Top Fundraisers — renders on the frontend.', 'ambassadorhub' )
				. '</div>';
		}

		$count      = max( 1, min( 50, (int) ( $attributes['count'] ?? 5 ) ) );
		$show_image = (bool) ( $attributes['showImage'] ?? true );
		$title      = trim( (string) ( $attributes['title'] ?? __( 'Top fundraisers', 'ambassadorhub' ) ) );
		$title      = self::translate_template_title( $title );

		// Resolve root fundraiser: explicit preset > current root page > traverse to root.
		$root_id = (int) ( $attributes['presetRootId'] ?? $attributes['presetCampaignId'] ?? 0 );
		if ( $root_id <= 0 ) {
			$post = get_post();
			if ( $post && FundraisersModule::POST_TYPE === $post->post_type ) {
				if ( 0 === (int) $post->post_parent ) {
					$root_id = $post->ID;
				} else {
					$root_id = FundraisersHelpers::get_root_fundraiser_id( $post->ID );
				}
			}
		}

		if ( $root_id <= 0 ) {
			return '';
		}

		$fundraisers = self::get_top_fundraisers( $root_id, $count );

		if ( empty( $fundraisers ) ) {
			return '<div class="ambh-top-fundraisers ambh-top-fundraisers--empty">'
				. '<p>' . esc_html__( 'No fundraisers yet — be the first!', 'ambassadorhub' ) . '</p>'
				. '</div>';
		}

		$title_html = '' !== $title
			? '<h3 class="ambh-tf-title">' . esc_html( $title ) . '</h3>'
			: '';

		$items = '';
		foreach ( $fundraisers as $rank => $f ) {
			$items .= self::render_item( $f, $rank + 1, $show_image );
		}

		return '<div class="ambh-top-fundraisers">'
			. $title_html
			. '<ol class="ambh-tf-list">' . $items . '</ol>'
			. '</div>';
	}

	// -------------------------------------------------------------------------
	// Item rendering
	// -------------------------------------------------------------------------

	private static function render_item( array $f, int $rank, bool $show_image ): string {
		$target = $f['target_amount'];
		$raised = $f['total_raised'];
		$pct    = $target > 0.0 ? min( 100, round( ( $raised / $target ) * 100 ) ) : 0;

		// Rank badge with gold/silver/bronze colours set via CSS data attribute.
		$rank_badge = '<span class="ambh-tf-rank" data-rank="' . esc_attr( (string) $rank ) . '">'
			. esc_html( (string) $rank )
			. '</span>';

		// Optional thumbnail.
		$thumb_html = '';
		if ( $show_image ) {
			$thumb_url = $f['thumbnail_url'] ?? '';
			if ( '' !== $thumb_url ) {
				$thumb_html = '<span class="ambh-tf-thumb">'
					. '<img src="' . esc_url( $thumb_url ) . '" alt="' . esc_attr( $f['title'] ) . '" loading="lazy">'
					. '</span>';
			} else {
				$thumb_html = '<span class="ambh-tf-thumb ambh-tf-thumb--placeholder" aria-hidden="true">'
					. '<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="12" cy="8" r="4" stroke="currentColor" stroke-width="1.5"/><path d="M4 20c0-4 3.6-7 8-7s8 3 8 7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>'
					. '</span>';
			}
		}

		// Stats.
		$raised_fmt = SettingsManager::format_currency( $raised, 0 );
		$stats_html = '<span class="ambh-tf-raised">' . esc_html( $raised_fmt ) . '</span>';
		if ( $target > 0.0 ) {
			$stats_html .= '<span class="ambh-tf-target">'
				. esc_html(
					sprintf(
					/* translators: %s: formatted target amount */
						__( 'of %s', 'ambassadorhub' ),
						SettingsManager::format_currency( $target, 0 )
					)
				)
				. '</span>';
		}

		// Progress bar.
		$bar_html = $target > 0.0
			? '<div class="ambh-tf-progress" role="progressbar" aria-valuenow="' . esc_attr( (string) $pct ) . '" aria-valuemin="0" aria-valuemax="100">'
				. '<div class="ambh-tf-progress__fill" style="width:' . esc_attr( $pct . '%' ) . '"></div>'
				. '</div>'
			: '';

		return '<li class="ambh-tf-item ambh-tf-item--rank-' . esc_attr( (string) $rank ) . '">'
			. '<a class="ambh-tf-item__link" href="' . esc_url( $f['permalink'] ) . '">'
			. $rank_badge
			. $thumb_html
			. '<span class="ambh-tf-body">'
			. '<span class="ambh-tf-name">' . esc_html( $f['title'] ) . '</span>'
			. '<span class="ambh-tf-stats">' . $stats_html . '</span>'
			. $bar_html
			. '</span>'
			. '</a>'
			. '</li>';
	}

	// -------------------------------------------------------------------------
	// Data
	// -------------------------------------------------------------------------

	/**
	 * Get the top N fundraisers for a root, ranked by amount raised.
	 * Only direct children of the root are ranked so the leaderboard reflects
	 * ambassador-level effort.
	 *
	 * @param int $root_id Root fundraiser post ID.
	 * @param int $count   Maximum items to return.
	 *
	 * @return array
	 */
	private static function get_top_fundraisers( int $root_id, int $count ): array {
		global $wpdb;

		$donations_table = esc_sql( $wpdb->prefix . 'ambh_donations' );

		// Direct children of the root are the entities to rank.
		$top_level_ids = get_posts(
			array(
				'post_type'      => FundraisersModule::POST_TYPE,
				'post_parent'    => $root_id,
				'post_status'    => 'publish',
				'posts_per_page' => -1,
				'fields'         => 'ids',
			)
		);

		if ( empty( $top_level_ids ) ) {
			return array();
		}

		$top_level_ids = array_map( 'intval', $top_level_ids );

		// Pre-fetch all descendants for efficient subtree aggregation.
		$all_descendants = self::get_all_descendant_ids( $root_id );

		$fundraisers = array();
		foreach ( $top_level_ids as $top_id ) {
			// Collect the whole sub-tree: this child + all its descendants.
			$tree_ids   = self::get_subtree_ids( $top_id, $all_descendants );
			$tree_ids[] = $top_id;
			$tree_ids   = array_unique( array_map( 'intval', $tree_ids ) );

			$placeholders = implode( ',', array_fill( 0, count( $tree_ids ), '%d' ) );
			$query_args   = array_merge( $tree_ids, array( 'completed' ) );
			$total        = (float) $wpdb->get_var(
				$wpdb->prepare(
					"SELECT COALESCE(SUM(amount),0) FROM {$donations_table}
				 WHERE fundraiser_id IN ({$placeholders}) AND status = %s",
					...$query_args
				)
			);

			$post          = get_post( $top_id );
			$target_amount = (float) get_post_meta( $top_id, '_ambh_target_amount', true );
			$thumbnail_url = get_the_post_thumbnail_url( $top_id, 'thumbnail' );

			$fundraisers[] = array(
				'id'            => $top_id,
				'title'         => $post->post_title,
				'permalink'     => (string) get_permalink( $top_id ),
				'thumbnail_url' => (string) ( $thumbnail_url ? $thumbnail_url : '' ),
				'total_raised'  => $total,
				'target_amount' => $target_amount,
			);
		}

		// Sort by total raised descending, then slice.
		usort( $fundraisers, static fn( array $a, array $b ): int => $b['total_raised'] <=> $a['total_raised'] );

		return array_slice( $fundraisers, 0, $count );
	}

	/**
	 * Recursively collect all descendant IDs of a root fundraiser.
	 *
	 * @param int $parent_id Parent fundraiser ID.
	 *
	 * @return int[]
	 */
	private static function get_all_descendant_ids( int $parent_id ): array {
		$ids      = array();
		$children = get_posts(
			array(
				'post_type'      => FundraisersModule::POST_TYPE,
				'post_parent'    => $parent_id,
				'posts_per_page' => -1,
				'fields'         => 'ids',
				'post_status'    => 'publish',
			)
		);
		foreach ( $children as $child_id ) {
			$ids[] = (int) $child_id;
			$ids   = array_merge( $ids, self::get_all_descendant_ids( (int) $child_id ) );
		}
		return $ids;
	}

	/**
	 * Collect all descendant IDs of $root_id from a pre-fetched pool.
	 *
	 * @param int   $root_id Root fundraiser ID.
	 * @param int[] $pool    All descendant IDs for the parent root.
	 *
	 * @return int[]
	 */
	private static function get_subtree_ids( int $root_id, array $pool ): array {
		$children = array();
		foreach ( $pool as $id ) {
			$post = get_post( $id );
			if ( $post && (int) $post->post_parent === $root_id ) {
				$children[] = $id;
				$children   = array_merge( $children, self::get_subtree_ids( $id, $pool ) );
			}
		}
		return $children;
	}

	/**
	 * Translate known template-provided titles.
	 *
	 * @param string $title Raw title from block attributes.
	 *
	 * @return string
	 */
	private static function translate_template_title( string $title ): string {
		$map = array(
			'Top fundraisers' => __( 'Top fundraisers', 'ambassadorhub' ),
		);

		return $map[ $title ] ?? $title;
	}
}
