<?php
/**
 * Fundraiser Stats Block
 *
 * Displays donation totals, progress, and team count for a fundraiser and all
 * its descendants. Works for both root (campaign-level) fundraisers and
 * individual/team fundraisers — the same block adapts based on context.
 *
 * @package AmbassadorHub\Fundraisers\Blocks\Stats
 */

namespace AmbassadorHub\Fundraisers\Blocks\Stats;

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\Donations\DonationsModule;
use AmbassadorHub\Fundraisers\Admin\Settings\SettingsManager;
use AmbassadorHub\Fundraisers\FundraisersHelpers;
use AmbassadorHub\Fundraisers\FundraisersModule;

/**
 * Stats block registration and rendering.
 */
final class Block {

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

	/**
	 * Register the block.
	 *
	 * @return void
	 */
	public static function register_block(): void {
		$handle    = 'ambh-fundraiser-stats-block';
		$block_dir = AMBASSADOR_HUB_PATH . 'src/Fundraisers/Blocks/Stats/';
		$block_url = AMBASSADOR_HUB_URL . 'src/Fundraisers/Blocks/Stats/';

		$script_file = $block_dir . 'index.js';
		if ( file_exists( $script_file ) ) {
			wp_register_script(
				$handle,
				$block_url . 'index.js',
				array( 'wp-blocks', 'wp-i18n', 'wp-element', 'wp-server-side-render', 'wp-block-editor' ),
				filemtime( $script_file ),
				true
			);
		}

		wp_register_style(
			'ambh-fundraiser-stats',
			$block_url . 'style.css',
			array(),
			AMBASSADOR_HUB_VERSION
		);

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

		if ( file_exists( $script_file ) ) {
			$block_args['editor_script'] = $handle;
		}

		register_block_type( $block_dir, $block_args );

		add_shortcode( 'ambh_fundraiser_stats', array( self::class, 'render_shortcode' ) );
	}

	/**
	 * Render the block.
	 *
	 * @param array $attributes Block attributes.
	 *
	 * @return string Block HTML.
	 */
	public static function render_block( array $attributes = array() ): string {
		if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
			return '<div style="padding: 20px; background: #f0f0f1; border: 1px solid #ddd; text-align: center;">'
				. esc_html__( 'Fundraiser statistics.', 'ambassadorhub' )
				. '</div>';
		}

		$stats = self::resolve_stats( $attributes );
		if ( array() === $stats ) {
			return '';
		}

		return self::render_stats_html( $stats );
	}

	/**
	 * Resolve fundraiser statistics from block attributes/context.
	 *
	 * @param array $attributes Block attributes.
	 *
	 * @return array
	 */
	public static function resolve_stats( array $attributes = array() ): array {
		$fundraiser_id = self::resolve_fundraiser_id( $attributes );

		if ( ! $fundraiser_id ) {
			return array();
		}

		$stats                   = self::get_fundraiser_stats( $fundraiser_id );
		$stats['team_count']     = self::get_fundraiser_team_count( $fundraiser_id );
		$stats['days_remaining'] = self::get_fundraiser_days_remaining( $fundraiser_id );

		return $stats;
	}

	/**
	 * Resolve the fundraiser ID from block attributes or the current page context.
	 *
	 * @param array $attributes Block attributes.
	 *
	 * @return int 0 when no context can be determined.
	 */
	public static function resolve_fundraiser_id( array $attributes = array() ): int {
		// Preset attribute — also accept legacy campaignId as alias.
		if ( ! empty( $attributes['fundraiserId'] ) && (int) $attributes['fundraiserId'] > 0 ) {
			return (int) $attributes['fundraiserId'];
		}
		if ( ! empty( $attributes['campaignId'] ) && (int) $attributes['campaignId'] > 0 ) {
			return (int) $attributes['campaignId'];
		}

		$current_id = (int) get_the_ID();
		if ( $current_id > 0 && FundraisersModule::POST_TYPE === get_post_type( $current_id ) ) {
			return $current_id;
		}

		return 0;
	}

	/**
	 * Count team fundraisers nested beneath a fundraiser (direct and indirect).
	 *
	 * @param int $fundraiser_id Fundraiser ID.
	 *
	 * @return int
	 */
	public static function get_fundraiser_team_count( int $fundraiser_id ): int {
		$descendant_ids = self::get_fundraiser_children_ids_recursive( $fundraiser_id );
		if ( array() === $descendant_ids ) {
			return 0;
		}

		$count = 0;
		foreach ( $descendant_ids as $child_id ) {
			if ( FundraisersHelpers::fundraiser_is_active_team( (int) $child_id ) ) {
				++$count;
			}
		}

		return $count;
	}

	/**
	 * Return days remaining for a fundraiser, or null when no end date is set.
	 *
	 * Returns 0 when the fundraiser is already closed.
	 *
	 * @param int $fundraiser_id Fundraiser ID.
	 *
	 * @return int|null
	 */
	public static function get_fundraiser_days_remaining( int $fundraiser_id ): ?int {
		if ( ! FundraisersHelpers::fundraiser_is_open( $fundraiser_id ) ) {
			return 0;
		}

		$end_date = FundraisersHelpers::get_effective_end_date_gmt( $fundraiser_id );
		if ( ! $end_date ) {
			return null;
		}

		return max( 0, (int) ceil( ( strtotime( $end_date ) - time() ) / DAY_IN_SECONDS ) );
	}

	/**
	 * Render the stats HTML output.
	 *
	 * @param array $stats Stats array.
	 *
	 * @return string
	 */
	private static function render_stats_html( array $stats ): string {
		$format = SettingsManager::get_currency_format();
		$dec    = $format['decimal'];
		$thou   = $format['thousands'];

		$wrapper_attrs = get_block_wrapper_attributes(
			array(
				'class' => 'fundraiser-stats-block',
			)
		);
		ob_start();
		?>
		<div <?php echo $wrapper_attrs; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- sanitized by get_block_wrapper_attributes(). ?>>
			<div class="stats-container">
				<div class="stat-card stat-primary">
					<div class="stat-value"><?php echo esc_html( SettingsManager::format_currency( $stats['total_raised'] ) ); ?></div>
					<div class="stat-label"><?php esc_html_e( 'Total raised', 'ambassadorhub' ); ?></div>
				</div>

				<div class="stat-card">
					<div class="stat-value"><?php echo number_format( $stats['donation_count'], 0, $dec, $thou ); ?></div>
					<div class="stat-label"><?php echo 1 === $stats['donation_count'] ? esc_html__( 'Donation', 'ambassadorhub' ) : esc_html__( 'Donations', 'ambassadorhub' ); ?></div>
				</div>
			</div>

			<?php if ( $stats['target'] > 0 ) : ?>
				<div class="progress-section">
					<div class="progress-header">
						<span class="progress-label"><?php echo ! empty( $stats['closed'] ) ? esc_html__( 'Result', 'ambassadorhub' ) : esc_html__( 'Progress', 'ambassadorhub' ); ?></span>
						<span class="progress-percentage"><?php echo number_format( $stats['progress'], 1, $dec, $thou ); ?>%</span>
					</div>
					<div class="progress-bar-wrapper">
						<div class="progress-bar-fill" style="width: <?php echo esc_attr( min( $stats['progress'], 100 ) ); ?>%;"></div>
					</div>
					<div class="progress-footer">
						<span><?php echo esc_html( SettingsManager::format_currency( $stats['total_raised'] ) ); ?> <?php esc_html_e( 'of', 'ambassadorhub' ); ?> <?php echo esc_html( SettingsManager::format_currency( $stats['target'] ) ); ?></span>
					</div>
				</div>
			<?php endif; ?>
		</div>
		<?php
		return ob_get_clean();
	}

	/**
	 * Render shortcode.
	 *
	 * @param array $atts Shortcode attributes.
	 *
	 * @return string
	 */
	public static function render_shortcode( $atts ): string {
		$atts = shortcode_atts( array( 'id' => get_the_ID() ), $atts );
		return self::render_block( array( 'fundraiserId' => intval( $atts['id'] ) ) );
	}

	/**
	 * Get fundraiser statistics, aggregating across all descendants.
	 *
	 * For root fundraisers (post_parent=0), the financial goal stored in
	 * `_ambh_financial_goal` is used as the target; for child fundraisers,
	 * `_ambh_target_amount` is used.
	 *
	 * @param int $fundraiser_id Fundraiser ID.
	 *
	 * @return array
	 */
	private static function get_fundraiser_stats( int $fundraiser_id ): array {
		global $wpdb;
		$table_name = DonationsModule::get_table_name();

		$post    = get_post( $fundraiser_id );
		$is_root = $post && 0 === (int) $post->post_parent;

		// Root fundraisers use the campaign-level financial goal; others use their own target.
		if ( $is_root ) {
			$target = (float) get_post_meta( $fundraiser_id, '_ambh_financial_goal', true );
			if ( ! $target ) {
				$target = (float) get_post_meta( $fundraiser_id, '_ambh_target_amount', true );
			}
		} else {
			$target = (float) get_post_meta( $fundraiser_id, '_ambh_target_amount', true );
		}

		$child_ids = self::get_fundraiser_children_ids_recursive( $fundraiser_id );
		$all_ids   = array_merge( array( $fundraiser_id ), $child_ids );

		$placeholders = implode( ', ', array_fill( 0, count( $all_ids ), '%d' ) );

		$total_raised = (float) $wpdb->get_var(
			$wpdb->prepare(
				"SELECT COALESCE(SUM(amount), 0) FROM $table_name WHERE fundraiser_id IN ($placeholders) AND status = 'completed'",
				...$all_ids
			)
		);

		$donation_count = (int) $wpdb->get_var(
			$wpdb->prepare(
				"SELECT COUNT(*) FROM $table_name WHERE fundraiser_id IN ($placeholders) AND status = 'completed'",
				...$all_ids
			)
		);

		$progress = $target > 0 ? ( $total_raised / $target ) * 100 : 0.0;

		$unique_owners = array();
		foreach ( $child_ids as $child_id ) {
			$owner_id = (int) get_post_field( 'post_author', $child_id );
			if ( $owner_id ) {
				$unique_owners[ $owner_id ] = true;
			}
		}
		$current_owner = (int) get_post_field( 'post_author', $fundraiser_id );
		if ( $current_owner ) {
			$unique_owners[ $current_owner ] = true;
		}

		return array(
			'total_raised'   => $total_raised,
			'target'         => $target,
			'progress'       => $progress,
			'donation_count' => $donation_count,
			'child_count'    => count( $unique_owners ),
			'closed'         => ! FundraisersHelpers::fundraiser_is_open( $fundraiser_id ),
		);
	}

	/**
	 * Recursively collect all descendant fundraiser IDs (any status).
	 *
	 * @param int $fundraiser_id Parent fundraiser ID.
	 *
	 * @return int[]
	 */
	private static function get_fundraiser_children_ids_recursive( int $fundraiser_id ): array {
		$all_children = array();

		$children = get_posts(
			array(
				'post_type'      => FundraisersModule::POST_TYPE,
				'post_parent'    => $fundraiser_id,
				'posts_per_page' => -1,
				'fields'         => 'ids',
				'post_status'    => 'any',
			)
		);

		$all_children = array_merge( $all_children, $children );

		foreach ( $children as $child_id ) {
			$all_children = array_merge( $all_children, self::get_fundraiser_children_ids_recursive( (int) $child_id ) );
		}

		return $all_children;
	}
}
