<?php
/**
 * Campaign Grid Block
 *
 * Displays published (and optionally ended) campaigns as a responsive card
 * grid. Each card shows the campaign featured image, title, progress bar, and
 * an optional status badge. Mirrors the FundraiserGrid block's patterns.
 *
 * @package AmbassadorHub\Campaigns\Blocks\CampaignGrid
 */

namespace AmbassadorHub\Campaigns\Blocks\CampaignGrid;

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

// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare

use AmbassadorHub\Fundraisers\Admin\Settings\SettingsManager;
use AmbassadorHub\Donations\DonationsModule;
use AmbassadorHub\Fundraisers\FundraisersModule;

/**
 * Campaign Grid block registration and server-side rendering.
 */
final class Block {

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

	/**
	 * Initialize the block.
	 *
	 * @return void
	 */
	public static function init(): void {
		add_action( 'init', array( self::class, 'register_block' ) );
	}

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

	/**
	 * Register block type, editor script, and styles.
	 *
	 * @return void
	 */
	public static function register_block(): void {
		$block_dir = AMBASSADOR_HUB_PATH . 'src/Campaigns/Blocks/CampaignGrid/';
		$block_url = AMBASSADOR_HUB_URL . 'src/Campaigns/Blocks/CampaignGrid/';

		$editor_handle = 'ambh-campaign-grid-editor';
		$editor_file   = $block_dir . 'index.js';
		if ( file_exists( $editor_file ) ) {
			wp_register_script(
				$editor_handle,
				$block_url . 'index.js',
				array( 'wp-blocks', 'wp-i18n', 'wp-element', 'wp-block-editor', 'wp-components', 'wp-server-side-render' ),
				(string) filemtime( $editor_file ),
				true
			);
		}

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

		$block_args = array(
			'style'           => 'ambh-campaign-grid',
			'render_callback' => array( self::class, 'render_block' ),
		);

		if ( file_exists( $editor_file ) ) {
			$block_args['editor_script'] = $editor_handle;
		}

		register_block_type( $block_dir, $block_args );
	}

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

	/**
	 * Server-side render callback.
	 *
	 * @param array $attributes Block attributes from block.json.
	 *
	 * @return string Rendered HTML.
	 */
	public static function render_block( array $attributes = array() ): string {

		// Lightweight editor placeholder.
		if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
			return '<div class="ambh-campaign-grid ambh-block-preview">'
				. '<p class="ambh-block-preview__label">' . esc_html__( 'Campaign Grid', 'ambassadorhub' ) . '</p>'
				. '<p class="ambh-block-preview__meta">' . esc_html__( 'Renders campaign cards on the frontend.', 'ambassadorhub' ) . '</p>'
				. '</div>';
		}

		$columns       = max( 1, min( 6, (int) ( $attributes['columns'] ?? 3 ) ) );
		$max_items     = max( 0, (int) ( $attributes['maxItems'] ?? 0 ) );
		$order_by      = in_array( $attributes['orderBy'] ?? '', array( 'date', 'title', 'raised' ), true ) ? $attributes['orderBy'] : 'date';
		$show_ended    = ! empty( $attributes['showEnded'] );
		$show_progress = $attributes['showProgress'] ?? true;
		$show_status   = $attributes['showStatusBadge'] ?? true;

		$campaigns = self::query_campaigns( $order_by, $max_items, $show_ended );

		if ( empty( $campaigns ) ) {
			return '';
		}

		$cards = '';
		foreach ( $campaigns as $campaign ) {
			$cards .= self::render_card( $campaign, (bool) $show_progress, (bool) $show_status );
		}

		return '<div class="ambh-campaign-grid" style="--ambh-cg-columns:' . esc_attr( (string) $columns ) . ';">'
			. $cards
			. '</div>';
	}

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

	/**
	 * Query root fundraisers and enrich each with aggregate donation totals.
	 *
	 * @param string $order_by   Sort field: 'date', 'title', or 'raised'.
	 * @param int    $max_items  Maximum items (0 = all).
	 * @param bool   $show_ended Whether to include ended root fundraisers.
	 *
	 * @return array List of enriched root fundraiser data maps.
	 */
	private static function query_campaigns( string $order_by, int $max_items, bool $show_ended ): array {
		global $wpdb;

		$statuses = array( 'publish' );
		if ( $show_ended ) {
			$statuses[] = 'ended';
		}

		$wp_orderby = 'title' === $order_by ? 'title' : 'date';
		$wp_order   = 'title' === $order_by ? 'ASC' : 'DESC';

		$query_args = array(
			'post_type'      => FundraisersModule::POST_TYPE,
			'post_status'    => $statuses,
			'post_parent'    => 0,
			'posts_per_page' => $max_items > 0 ? $max_items : -1,
			'orderby'        => $wp_orderby,
			'order'          => $wp_order,
			'fields'         => 'ids',
		);

		$query = new \WP_Query( $query_args );
		$ids   = array_map( 'intval', $query->posts ? $query->posts : array() );
		wp_reset_postdata();

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

		// Build a map of root_id → all descendant IDs (including root itself).
		$root_to_all_ids = array();
		$all_ids         = array();
		foreach ( $ids as $root_id ) {
			$descendants                 = self::get_all_fundraiser_ids( $root_id );
			$root_to_all_ids[ $root_id ] = $descendants;
			$all_ids                     = array_merge( $all_ids, $descendants );
		}
		$all_ids = array_unique( $all_ids );

		// Aggregate donation totals for all IDs in one SQL query.
		$donations_table  = DonationsModule::get_table_name();
		$ids_placeholders = implode( ', ', array_fill( 0, count( $all_ids ), '%d' ) );

		$rows = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT fundraiser_id,
					COALESCE( SUM( amount ), 0 ) AS total_raised,
					COUNT( id ) AS donation_count
				FROM {$donations_table}
				WHERE fundraiser_id IN ({$ids_placeholders})
				  AND status = 'completed'
				GROUP BY fundraiser_id",
				$all_ids
			)
		);

		$per_fnd_totals = array();
		foreach ( $rows as $row ) {
			$per_fnd_totals[ (int) $row->fundraiser_id ] = array(
				'total_raised'   => (float) $row->total_raised,
				'donation_count' => (int) $row->donation_count,
			);
		}

		// Sum per-fundraiser totals up to each root.
		$root_totals = array();
		foreach ( $ids as $root_id ) {
			$total_raised = 0.0;
			$donations    = 0;
			foreach ( $root_to_all_ids[ $root_id ] as $fid ) {
				$total_raised += $per_fnd_totals[ $fid ]['total_raised'] ?? 0.0;
				$donations    += $per_fnd_totals[ $fid ]['donation_count'] ?? 0;
			}
			$root_totals[ $root_id ] = array(
				'total_raised'   => $total_raised,
				'donation_count' => $donations,
			);
		}

		// Build enriched list.
		$campaigns = array();
		foreach ( $ids as $id ) {
			$post = get_post( $id );
			if ( ! $post ) {
				continue;
			}

			$goal         = (float) get_post_meta( $id, FundraisersModule::META_FINANCIAL_GOAL, true );
			$total_raised = $root_totals[ $id ]['total_raised'] ?? 0.0;
			$donations    = $root_totals[ $id ]['donation_count'] ?? 0;
			$progress     = $goal > 0 ? min( ( $total_raised / $goal ) * 100, 100 ) : 0.0;

			$campaigns[] = array(
				'id'           => $id,
				'title'        => get_the_title( $id ),
				'permalink'    => get_permalink( $id ),
				'status'       => $post->post_status,
				'goal'         => $goal,
				'total_raised' => $total_raised,
				'donations'    => $donations,
				'progress'     => $progress,
				'thumb_id'     => (int) get_post_thumbnail_id( $id ),
			);
		}

		if ( 'raised' === $order_by ) {
			usort( $campaigns, static fn( $a, $b ) => $b['total_raised'] <=> $a['total_raised'] );
		}

		return $campaigns;
	}

	/**
	 * Recursively get all fundraiser IDs under a parent (including the parent itself).
	 *
	 * @param int $fundraiser_id Starting fundraiser ID.
	 *
	 * @return int[]
	 */
	private static function get_all_fundraiser_ids( int $fundraiser_id ): array {
		$ids      = array( $fundraiser_id );
		$children = get_posts(
			array(
				'post_type'      => FundraisersModule::POST_TYPE,
				'post_parent'    => $fundraiser_id,
				'posts_per_page' => -1,
				'fields'         => 'ids',
				'post_status'    => 'any',
			)
		);
		foreach ( $children as $child_id ) {
			$ids = array_merge( $ids, self::get_all_fundraiser_ids( (int) $child_id ) );
		}
		return $ids;
	}

	// -------------------------------------------------------------------------
	// Card rendering
	// -------------------------------------------------------------------------

	/**
	 * Render a single campaign card.
	 *
	 * @param array $campaign     Enriched campaign data map.
	 * @param bool  $show_progress Whether to render the progress bar.
	 * @param bool  $show_status   Whether to render the status badge.
	 *
	 * @return string Card HTML.
	 */
	private static function render_card( array $campaign, bool $show_progress, bool $show_status ): string {
		ob_start();
		?>
		<article class="ambh-cg-card">
			<a href="<?php echo esc_url( $campaign['permalink'] ); ?>" class="ambh-cg-card__link">

				<?php // Featured image or placeholder. ?>
				<div class="ambh-cg-card__image">
					<?php if ( $campaign['thumb_id'] > 0 ) : ?>
						<?php
						echo wp_get_attachment_image(
							$campaign['thumb_id'],
							'medium',
							false,
							array(
								'loading' => 'lazy',
								'alt'     => esc_attr( $campaign['title'] ),
							)
						);
						?>
					<?php else : ?>
						<div class="ambh-cg-card__placeholder" aria-hidden="true">
							<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
								<rect x="3" y="5" width="18" height="14" rx="2"/>
								<circle cx="8.5" cy="9.5" r="1.5"/>
								<polyline points="21 15 16 10 5 21"/>
							</svg>
						</div>
					<?php endif; ?>
				</div>

				<div class="ambh-cg-card__body">

					<?php if ( $show_status && 'ended' === $campaign['status'] ) : ?>
						<span class="ambh-cg-card__badge ambh-cg-card__badge--ended">
							<?php esc_html_e( 'Ended', 'ambassadorhub' ); ?>
						</span>
					<?php endif; ?>

					<h3 class="ambh-cg-card__title"><?php echo esc_html( $campaign['title'] ); ?></h3>

					<div class="ambh-cg-card__stats">
						<span class="ambh-cg-card__raised">
							<?php echo esc_html( SettingsManager::format_currency( $campaign['total_raised'] ) ); ?>
						</span>
						<?php if ( $campaign['goal'] > 0 ) : ?>
							<span class="ambh-cg-card__target">
								<?php
								echo esc_html(
									sprintf(
										/* translators: %s: formatted goal amount */
										__( 'of %s', 'ambassadorhub' ),
										SettingsManager::format_currency( $campaign['goal'] )
									)
								);
								?>
							</span>
						<?php endif; ?>
					</div>

					<?php if ( $show_progress && $campaign['goal'] > 0 ) : ?>
						<div class="ambh-cg-card__progress" role="progressbar" aria-valuenow="<?php echo esc_attr( (string) round( $campaign['progress'] ) ); ?>" aria-valuemin="0" aria-valuemax="100" aria-label="<?php echo esc_attr( round( $campaign['progress'] ) . '%' ); ?>">
							<div class="ambh-cg-card__progress-fill" style="width:<?php echo esc_attr( (string) min( $campaign['progress'], 100 ) ); ?>%;"></div>
						</div>
					<?php endif; ?>

				</div>
			</a>
		</article>
		<?php
		return ob_get_clean();
	}
}
