<?php
/**
 * PostGate Dashboard REST API Controller Class
 *
 * This file contains the REST API controller for the admin dashboard,
 * providing endpoints for statistics, revenue charts, and payment data.
 * Handles data aggregation and caching for optimal dashboard performance.
 *
 * @package PostGate
 * @subpackage API
 * @since 1.0.0
 * @author QodeSphere
 * @license GPL-2.0+
 */

defined( 'ABSPATH' ) || exit;

/**
 * PostGate Dashboard REST API Controller Class
 *
 * Provides REST API endpoints for the admin dashboard, delivering real-time
 * statistics, revenue analytics, and payment information. Implements caching
 * strategies to optimize performance and reduce database load.
 *
 * This controller serves as the data layer for the PostGate admin dashboard,
 * aggregating information from the orders table and WordPress user data.
 * It processes and formats data for frontend consumption, ensuring efficient
 * delivery of analytics and payment information.
 *
 * Endpoints:
 * - GET /dashboard/stats - Overall statistics (revenue, subscriptions, customers)
 * - GET /dashboard/revenue-chart - Time-series revenue data
 * - GET /dashboard/latest-payments - Recent payment transactions
 * - GET /dashboard/top-content - Top performing content by revenue
 *
 * Key Features:
 * - Comprehensive dashboard statistics with currency formatting
 * - Flexible revenue charts (daily, weekly, monthly)
 * - Latest payments feed with user and post information
 * - Object caching for improved performance
 * - Manual cache flush capability
 * - Permission-based access control
 * - Automatic cache versioning
 * - Support for multiple time periods
 *
 * @since 1.0.0
 */
class PostGate_Dashboard_Controller {

	/**
	 * Namespace for the REST API.
	 *
	 * @var string
	 */
	const NAMESPACE = 'postgate/v1';

	/**
	 * Object cache group.
	 *
	 * @since 1.0.0
	 */
	private const CACHE_GROUP = 'postgate_dashboard';

	/**
	 * Default cache TTL (seconds).
	 *
	 * @since 1.0.0
	 */
	private const CACHE_TTL = 300; // 5 minutes.

	/**
	 * Initialize the dashboard controller.
	 *
	 * Registers REST API routes for dashboard endpoints when WordPress
	 * initializes the REST API. Sets up all dashboard-related API routes.
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public static function init() {
		add_action( 'rest_api_init', [ __CLASS__, 'register_routes' ] );
	}

	/**
	 * Register REST API routes for dashboard endpoints.
	 *
	 * Defines all available dashboard API endpoints including:
	 * - Dashboard statistics (revenue, subscriptions, customers)
	 * - Revenue charts with period-based data
	 * - Latest payments list with user information
	 * - Top performing content by revenue
	 *
	 * All endpoints require manage_options capability.
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public static function register_routes() {
		register_rest_route(
			self::NAMESPACE ,
			'/dashboard/stats',
			[
				'methods' => 'GET',
				'callback' => [ __CLASS__, 'get_dashboard_stats' ],
				'permission_callback' => [ __CLASS__, 'check_permission' ],
			]
		);

		register_rest_route(
			self::NAMESPACE ,
			'/dashboard/revenue-chart',
			[
				'methods' => 'GET',
				'callback' => [ __CLASS__, 'get_revenue_chart' ],
				'permission_callback' => [ __CLASS__, 'check_permission' ],
				'args' => [
					'period' => [
						'type' => 'string',
						'enum' => [ 'day', 'week', 'month' ],
						'default' => 'week',
					],
				],
			]
		);

		register_rest_route(
			self::NAMESPACE ,
			'/dashboard/latest-payments',
			[
				'methods' => 'GET',
				'callback' => [ __CLASS__, 'get_latest_payments' ],
				'permission_callback' => [ __CLASS__, 'check_permission' ],
				'args' => [
					'limit' => [
						'type' => 'integer',
						'default' => 10,
						'minimum' => 1,
						'maximum' => 50,
					],
				],
			]
		);

		register_rest_route(
			self::NAMESPACE ,
			'/dashboard/top-content',
			[
				'methods' => 'GET',
				'callback' => [ __CLASS__, 'get_top_content' ],
				'permission_callback' => [ __CLASS__, 'check_permission' ],
				'args' => [
					'limit' => [
						'type' => 'integer',
						'default' => 10,
						'minimum' => 1,
						'maximum' => 50,
					],
				],
			]
		);
	}

	/**
	 * Check permission for dashboard access.
	 *
	 * Verifies that the current user has the manage_options capability,
	 * which is required to access all dashboard endpoints.
	 *
	 * @since 1.0.0
	 * @return bool True if user can manage options, false otherwise
	 */
	public static function check_permission() {
		return current_user_can( 'manage_options' );
	}

	/**
	 * Generate a cache key for dashboard data.
	 *
	 * Creates a unique cache key using MD5 hash of the suffix.
	 * Used to store and retrieve cached dashboard statistics.
	 *
	 * @since 1.0.0
	 * @param string $suffix Unique identifier for the cached data
	 * @return string Generated cache key
	 */
	private static function cache_key( string $suffix ): string {
		return 'postgate_dash_' . md5( $suffix );
	}

	/**
	 * Flush all dashboard caches.
	 *
	 * Clears cached dashboard statistics, revenue charts, latest payments,
	 * and top content data. Should be called after order status changes
	 * to ensure dashboard displays up-to-date information.
	 *
	 * Cleared caches:
	 * - Dashboard statistics
	 * - Revenue charts (all periods: day, week, month)
	 * - Latest payments (all limit variations)
	 * - Top content (all limit variations)
	 *
	 * @since 1.0.0
	 * @return void
	 */
	public static function flush_cache(): void {
		// Conservative approach: delete the known keys we generate.
		wp_cache_delete( self::cache_key( 'stats_v1' ), self::CACHE_GROUP );

		foreach ( [ 'day', 'week', 'month' ] as $period ) {
			wp_cache_delete( self::cache_key( 'revenue_chart_v1_' . $period ), self::CACHE_GROUP );
		}

		// Limits used by UI: you can expand if needed.
		foreach ( [ 10, 20, 30, 40, 50 ] as $limit ) {
			wp_cache_delete( self::cache_key( 'latest_payments_v1_' . $limit ), self::CACHE_GROUP );
			wp_cache_delete( self::cache_key( 'top_content_v1_' . $limit ), self::CACHE_GROUP );
		}
	}


	/**
	 * Get dashboard statistics.
	 *
	 * Retrieves comprehensive dashboard statistics including:
	 * - Monthly Recurring Revenue (MRR) and subscription count
	 * - One-time sales count and growth
	 * - Failed payments and retry attempts
	 * - Growth percentages compared to previous periods
	 *
	 * Results are cached for performance. Compares current 30-day period
	 * to previous 30-day period for growth calculations.
	 *
	 * @since 1.0.0
	 * @param WP_REST_Request $request REST request object
	 * @return WP_REST_Response Response object with dashboard statistics
	 */
	public static function get_dashboard_stats( WP_REST_Request $request ) {
		$cache_key = self::cache_key( 'stats_v1' );
		$cached = wp_cache_get( $cache_key, self::CACHE_GROUP );

		if ( false !== $cached && is_array( $cached ) ) {
			return rest_ensure_response( $cached );
		}

		global $wpdb;

		$orders_table = $wpdb->prefix . 'postgate_orders';

		$dt_30_days = gmdate( 'Y-m-d H:i:s', strtotime( '-30 days' ) );
		$dt_60_days = gmdate( 'Y-m-d H:i:s', strtotime( '-60 days' ) );
		$dt_7_days = gmdate( 'Y-m-d H:i:s', strtotime( '-7 days' ) );

		// MRR: sum of paid subscriptions (simple proxy).
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
		$mrr_data = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT SUM(amount_cents) AS total_cents, COUNT(*) AS count
						FROM %i
						WHERE type = %s
						AND status = %s",
				$orders_table,
				'subscription',
				'paid'
			),
			ARRAY_A
		);

		$mrr_cents = (int) ( $mrr_data['total_cents'] ?? 0 );
		$subscription_count = (int) ( $mrr_data['count'] ?? 0 );

		// Previous period: count of paid subscriptions created before 30 days ago.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
		$previous_mrr = (int) $wpdb->get_var(
			$wpdb->prepare(
				"SELECT COUNT(*)
				 		FROM %i
				 		WHERE type = %s
				   		AND status = %s
				   		AND created_at < %s",
				$orders_table,
				'subscription',
				'paid',
				$dt_30_days
			)
		);

		// One-off sales in last 30 days.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
		$one_off_sales = (int) $wpdb->get_var(
			$wpdb->prepare(
				"SELECT COUNT(*)
				 		FROM %i
				 		WHERE type = %s
				   		AND status = %s
				   		AND created_at >= %s",
				$orders_table,
				'one_time',
				'paid',
				$dt_30_days
			)
		);

		// Previous one-off period (30-60 days ago).
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
		$previous_one_off = (int) $wpdb->get_var(
			$wpdb->prepare(
				"SELECT COUNT(*)
				 		FROM %i
				 		WHERE type = %s
				   		AND status = %s
				   		AND created_at >= %s
				   		AND created_at < %s",
				$orders_table,
				'one_time',
				'paid',
				$dt_60_days,
				$dt_30_days
			)
		);

		// Failed payments in last 7 days.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
		$failed_payments = (int) $wpdb->get_var(
			$wpdb->prepare(
				"SELECT COUNT(*)
				 		FROM %i
				 		WHERE status = %s
				   		AND created_at >= %s",
				$orders_table,
				'failed',
				$dt_7_days
			)
		);

		// Retry attempts: failed payments with a payment_intent_id in last 7 days.
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
		$retry_count = (int) $wpdb->get_var(
			$wpdb->prepare(
				"SELECT COUNT(*)
				 		FROM %i
				 		WHERE status = %s
				   		AND created_at >= %s
				   		AND stripe_payment_intent_id IS NOT NULL",
				$orders_table,
				'failed',
				$dt_7_days
			)
		);

		$mrr_growth = self::calculate_growth( $subscription_count, $previous_mrr );
		$sales_growth = self::calculate_growth( $one_off_sales, $previous_one_off );

		$payload = [
			'mrr' => [
				'value' => $mrr_cents,
				'count' => $subscription_count,
				'growth' => $mrr_growth,
			],
			'subscriptions' => [
				'value' => $subscription_count,
				'growth' => $mrr_growth,
			],
			'one_off_sales' => [
				'value' => $one_off_sales,
				'growth' => $sales_growth,
			],
			'failed_payments' => [
				'value' => $failed_payments,
				'retries' => $retry_count,
			],
		];

		wp_cache_set( $cache_key, $payload, self::CACHE_GROUP, self::CACHE_TTL );

		return rest_ensure_response( $payload );
	}

	/**
	 * Get revenue chart data.
	 *
	 * Retrieves time-series revenue data for the specified period.
	 * Groups revenue by time intervals and provides both revenue totals
	 * and order counts for each period.
	 *
	 * Supported periods:
	 * - 'day': Last 24 hours, hourly intervals
	 * - 'week': Last 7 days, daily intervals (default)
	 * - 'month': Last 30 days, daily intervals
	 *
	 * Results are cached per period for performance.
	 *
	 * @since 1.0.0
	 * @param WP_REST_Request $request REST request object with 'period' parameter
	 * @return WP_REST_Response Response object with chart data array
	 */
	public static function get_revenue_chart( WP_REST_Request $request ) {
		$period = (string) ( $request->get_param( 'period' ) ?: 'week' );

		$cache_key = self::cache_key( 'revenue_chart_v1_' . $period );
		$cached = wp_cache_get( $cache_key, self::CACHE_GROUP );

		if ( false !== $cached && is_array( $cached ) ) {
			return rest_ensure_response( $cached );
		}

		global $wpdb;

		$orders_table = $wpdb->prefix . 'postgate_orders';
		// Determine date format and range based on period
		switch ( $period ) {
			case 'day':
				$date_format = '%Y-%m-%d %H:00:00';
				$interval = '-24 hours';
				$label_format = 'H:i';
				break;

			case 'month':
				$date_format = '%Y-%m-%d';
				$interval = '-30 days';
				$label_format = 'M j';
				break;

			case 'week':
			default:
				$date_format = '%Y-%m-%d';
				$interval = '-7 days';
				$label_format = 'D';
				$period = 'week';
				break;
		}

		$start_date = gmdate( 'Y-m-d H:i:s', strtotime( $interval ) );

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
		$results = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT
						DATE_FORMAT(created_at, %s) AS period, SUM(amount_cents) AS revenue_cents, COUNT(*) AS order_count
				 		FROM %i
				 		WHERE status = %s
				   		AND created_at >= %s
				 		GROUP BY period
				 		ORDER BY period ASC",
				$date_format,
				$orders_table,
				'paid',
				$start_date
			),
			ARRAY_A
		);

		$chart_data = [];
		foreach ( (array) $results as $row ) {
			$timestamp = strtotime( (string) ( $row['period'] ?? '' ) );

			$chart_data[] = [
				'name' => $timestamp ? wp_date( $label_format, $timestamp ) : '',
				'value' => (int) ( $row['revenue_cents'] ?? 0 ),
				'count' => (int) ( $row['order_count'] ?? 0 ),
			];
		}

		$payload = [
			'period' => $period,
			'data' => $chart_data,
		];

		wp_cache_set( $cache_key, $payload, self::CACHE_GROUP, self::CACHE_TTL );

		return rest_ensure_response( $payload );
	}

	/**
	 * Get latest payments list.
	 *
	 * Retrieves the most recent paid orders with enriched information including
	 * user details, post information, and formatted dates. Only returns successful
	 * payments in reverse chronological order.
	 *
	 * Each payment includes:
	 * - Order details (ID, type, status, amount)
	 * - User information (name, email)
	 * - Associated post (title, URL) if applicable
	 * - Formatted creation date
	 *
	 * Results are cached per limit for performance.
	 *
	 * @since 1.0.0
	 * @param WP_REST_Request $request REST request object with 'limit' parameter (1-50)
	 * @return WP_REST_Response Response object with payments array
	 */
	public static function get_latest_payments( WP_REST_Request $request ) {
		$limit = max( 1, min( 50, (int) $request->get_param( 'limit' ) ) );

		$cache_key = self::cache_key( 'latest_payments_v1_' . $limit );
		$cached = wp_cache_get( $cache_key, self::CACHE_GROUP );

		if ( false !== $cached && is_array( $cached ) ) {
			return rest_ensure_response( $cached );
		}

		global $wpdb;

		$orders_table = $wpdb->prefix . 'postgate_orders';

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
		$orders = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT
					id,
					user_id,
					type,
					status,
					amount_cents,
					product_name,
					origin_post_id,
					created_at
				 FROM %i
				 WHERE status = %s
				 ORDER BY created_at DESC
				 LIMIT %d",
				$orders_table,
				'paid',
				$limit
			),
			ARRAY_A
		);

		foreach ( $orders as &$order ) {
			$order['id'] = (int) $order['id'];
			$order['user_id'] = (int) $order['user_id'];
			$order['amount_cents'] = (int) $order['amount_cents'];

			$user = get_user_by( 'id', $order['user_id'] );
			if ( $user ) {
				$order['user_name'] = $user->display_name;
				$order['user_email'] = $user->user_email;
			} else {
				$order['user_name'] = __( 'Unknown User', 'postgate' );
				$order['user_email'] = '';
			}

			if ( ! empty( $order['origin_post_id'] ) ) {
				$post = get_post( (int) $order['origin_post_id'] );
				if ( $post ) {
					$order['post_title'] = $post->post_title;
					$order['post_url'] = get_permalink( $post );
				}
			}

			$order['created_at_formatted'] = wp_date(
				get_option( 'date_format' ) . ' ' . get_option( 'time_format' ),
				strtotime( (string) $order['created_at'] )
			);
		}
		unset( $order );

		wp_cache_set( $cache_key, $orders, self::CACHE_GROUP, 120 );

		return rest_ensure_response( $orders );
	}

	/**
	 * Get top performing content by revenue.
	 *
	 * Retrieves content ranked by total revenue generated, showing which
	 * posts are generating the most income. Aggregates all paid orders by
	 * origin post and calculates total revenue and purchase count.
	 *
	 * Each content item includes:
	 * - Post information (ID, title, URLs)
	 * - Product name
	 * - Total revenue in cents
	 * - Number of purchases
	 *
	 * Results are ordered by revenue descending and cached per limit.
	 *
	 * @since 1.0.0
	 * @param WP_REST_Request $request REST request object with 'limit' parameter (1-50)
	 * @return WP_REST_Response Response object with top content array
	 */
	public static function get_top_content( WP_REST_Request $request ) {
		$limit = max( 1, min( 50, (int) $request->get_param( 'limit' ) ) );

		$cache_key = self::cache_key( 'top_content_v1_' . $limit );
		$cached = wp_cache_get( $cache_key, self::CACHE_GROUP );

		if ( false !== $cached && is_array( $cached ) ) {
			return rest_ensure_response( $cached );
		}

		global $wpdb;

		$orders_table = $wpdb->prefix . 'postgate_orders';

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
		$results = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT
					origin_post_id,
					product_name,
					SUM(amount_cents) AS total_revenue_cents,
					COUNT(*) AS purchase_count
				 FROM %i
				 WHERE status = %s
				   AND origin_post_id IS NOT NULL
				 GROUP BY origin_post_id, product_name
				 ORDER BY total_revenue_cents DESC
				 LIMIT %d",
				$orders_table,
				'paid',
				$limit
			),
			ARRAY_A
		);

		$top_content = [];
		foreach ( (array) $results as $row ) {
			$post_id = (int) ( $row['origin_post_id'] ?? 0 );
			if ( $post_id <= 0 ) {
				continue;
			}

			$post = get_post( $post_id );
			if ( ! $post ) {
				continue;
			}

			$top_content[] = [
				'post_id' => $post_id,
				'post_title' => $post->post_title,
				'post_url' => get_permalink( $post ),
				'edit_url' => get_edit_post_link( $post_id, 'raw' ),
				'product_name' => (string) ( $row['product_name'] ?? '' ),
				'total_revenue_cents' => (int) ( $row['total_revenue_cents'] ?? 0 ),
				'purchase_count' => (int) ( $row['purchase_count'] ?? 0 ),
			];
		}

		wp_cache_set( $cache_key, $top_content, self::CACHE_GROUP, self::CACHE_TTL );

		return rest_ensure_response( $top_content );
	}

	/**
	 * Calculate growth percentage between two values.
	 *
	 * Compares current value to previous value and calculates the percentage
	 * change. Handles edge cases where previous value is zero.
	 *
	 * @since 1.0.0
	 * @param int $current  Current period value
	 * @param int $previous Previous period value
	 * @return float Growth percentage (positive for increase, negative for decrease)
	 */
	private static function calculate_growth( int $current, int $previous ): float {
		if ( 0 === $previous ) {
			return $current > 0 ? 100.0 : 0.0;
		}

		return round( ( ( $current - $previous ) / $previous ) * 100, 1 );
	}
}
