<?php
/**
 * Activity Repository
 *
 * Handles complex queries for member activity logs.
 * This repository provides data for activity panels in the frontend widget and admin areas.
 *
 * @package Awesomemotive\Rewardswp\Repositories
 * @since 1.2.0
 */

namespace Awesomemotive\Rewardswp\Repositories;

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

use Awesomemotive\Rewardswp\Abstracts\AbstractRepository;
use Awesomemotive\Rewardswp\Database\DatabaseHandler;
use Awesomemotive\Rewardswp\Database\Tables\MembersTable;
use Awesomemotive\Rewardswp\Database\Tables\PointsTable;
use Awesomemotive\Rewardswp\Database\Tables\ReferralsTable;
use Awesomemotive\Rewardswp\Database\Tables\RewardsTable;
use Awesomemotive\Rewardswp\Factories\IntegrationsFactory;
use Awesomemotive\Rewardswp\Interfaces\IntegrationInterface;
use function Awesomemotive\Rewardswp\Helpers\get_active_integration_source;
use function Awesomemotive\Rewardswp\Helpers\get_member;
use function Awesomemotive\Rewardswp\Helpers\rewardswp_log_error;

/**
 * Class ActivityRepository
 *
 * Provides activity log queries for members:
 * - Points activity (earned, spent, adjusted)
 * - Referrals activity (clicks, conversions)
 * - Rewards activity (issued, redeemed)
 *
 * All methods support pagination for lazy loading in the frontend.
 *
 * @since 1.2.0
 */
class ActivityRepository extends AbstractRepository {

	/**
	 * Members table instance.
	 *
	 * @since 1.2.0
	 *
	 * @var MembersTable
	 */
	private MembersTable $members_table;

	/**
	 * Points table instance.
	 *
	 * @since 1.2.0
	 *
	 * @var PointsTable
	 */
	private PointsTable $points_table;

	/**
	 * Referrals table instance.
	 *
	 * @since 1.2.0
	 *
	 * @var ReferralsTable
	 */
	private ReferralsTable $referrals_table;

	/**
	 * Rewards table instance.
	 *
	 * @since 1.2.0
	 *
	 * @var RewardsTable
	 */
	private RewardsTable $rewards_table;

	/**
	 * Integrations factory instance.
	 *
	 * @since 1.2.0
	 *
	 * @var IntegrationsFactory
	 */
	private IntegrationsFactory $integrations_factory;

	/**
	 * Constructor.
	 *
	 * @since 1.2.0
	 *
	 * @param DatabaseHandler    $db_handler         Database handler instance.
	 * @param MembersTable       $members_table      Members table instance.
	 * @param PointsTable        $points_table       Points table instance.
	 * @param ReferralsTable     $referrals_table    Referrals table instance.
	 * @param RewardsTable       $rewards_table      Rewards table instance.
	 * @param IntegrationsFactory $integrations_factory Integrations factory instance.
	 */
	public function __construct(
		DatabaseHandler $db_handler,
		MembersTable $members_table,
		PointsTable $points_table,
		ReferralsTable $referrals_table,
		RewardsTable $rewards_table,
		IntegrationsFactory $integrations_factory
	) {
		parent::__construct( $db_handler );
		$this->members_table        = $members_table;
		$this->points_table         = $points_table;
		$this->referrals_table      = $referrals_table;
		$this->rewards_table        = $rewards_table;
		$this->integrations_factory = $integrations_factory;
	}

	// ========================================================================
	// Points Activity
	// ========================================================================

	/**
	 * Get points activity for a member.
	 *
	 * Returns points entries with formatted data for display.
	 * Includes: earned, spent, adjusted, refunded, expired points.
	 *
	 * @since 1.2.0
	 *
	 * @param int $member_id Member ID.
	 * @param int $limit     Number of items to return. Default 10.
	 * @param int $offset    Offset for pagination. Default 0.
	 *
	 * @return array Array of points activity entries.
	 */
	public function get_points_activity( int $member_id, int $limit = 10, int $offset = 0 ): array {
		$sql = "
			SELECT
				p.id,
				p.points,
				p.type,
				p.source_type,
				p.source_id,
				p.status,
				p.note_public,
				p.created_at
			FROM {$this->points_table->get_prefixed_table_name()} p
			WHERE p.member_id = %d
			  AND p.status IN ('approved', 'pending')
			ORDER BY p.created_at DESC
			LIMIT %d OFFSET %d
		";

		return $this->query( $sql, [ $member_id, $limit, $offset ] );
	}

	/**
	 * Count total points activity entries for a member.
	 *
	 * Used for pagination to determine if there are more items to load.
	 *
	 * @since 1.2.0
	 *
	 * @param int $member_id Member ID.
	 *
	 * @return int Total count of points activity entries.
	 */
	public function count_points_activity( int $member_id ): int {
		$sql = "
			SELECT COUNT(*) as count
			FROM {$this->points_table->get_prefixed_table_name()} p
			WHERE p.member_id = %d
			  AND p.status IN ('approved', 'pending')
		";

		$result = $this->get_var( $sql, [ $member_id ] );

		return (int) ( $result ?? 0 );
	}

	// ========================================================================
	// Referrals Activity
	// ========================================================================

	/**
	 * Get referrals activity for a member (as advocate).
	 *
	 * Returns referrals where the member is the advocate.
	 * Includes: pending, completed, and rejected referrals.
	 *
	 * @since 1.2.0
	 *
	 * @param int $member_id Member ID.
	 * @param int $limit     Number of items to return. Default 10.
	 * @param int $offset    Offset for pagination. Default 0.
	 *
	 * @return array Array of referral activity entries.
	 */
	public function get_referrals_activity( int $member_id, int $limit = 10, int $offset = 0 ): array {
		$sql = "
			SELECT
				r.id,
				r.status,
				r.referred_email,
				r.referred_user_id,
				r.order_id,
				r.source,
				r.created_at,
				r.converted_at,
				rm.first_name as referred_first_name,
				rm.last_name as referred_last_name
			FROM {$this->referrals_table->get_prefixed_table_name()} r
			LEFT JOIN {$this->members_table->get_prefixed_table_name()} rm
				ON r.referred_user_id = rm.user_id
			WHERE r.advocate_id = %d
			ORDER BY r.created_at DESC
			LIMIT %d OFFSET %d
		";

		return $this->query( $sql, [ $member_id, $limit, $offset ] );
	}

	/**
	 * Count total referrals activity entries for a member.
	 *
	 * Used for pagination to determine if there are more items to load.
	 *
	 * @since 1.2.0
	 *
	 * @param int $member_id Member ID.
	 *
	 * @return int Total count of referral activity entries.
	 */
	public function count_referrals_activity( int $member_id ): int {
		$sql = "
			SELECT COUNT(*) as count
			FROM {$this->referrals_table->get_prefixed_table_name()} r
			WHERE r.advocate_id = %d
		";

		$result = $this->get_var( $sql, [ $member_id ] );

		return (int) ( $result ?? 0 );
	}

	// ========================================================================
	// Rewards Activity
	// ========================================================================

	/**
	 * Get rewards activity for a member.
	 *
	 * Returns rewards issued to the member, including redeemed ones.
	 * Per pitch: "Show redeemed rewards as well".
	 *
	 * @since 1.2.0
	 *
	 * @param int $member_id Member ID.
	 * @param int $limit     Number of items to return. Default 10.
	 * @param int $offset    Offset for pagination. Default 0.
	 *
	 * @return array Array of reward activity entries.
	 */
	public function get_rewards_activity( int $member_id, int $limit = 10, int $offset = 0 ): array {
		$sql = "
			SELECT
				rw.id,
				rw.status,
				rw.reward_type,
				rw.amount,
				rw.referral_id,
				rw.created_at,
				rw.applied_at
			FROM {$this->rewards_table->get_prefixed_table_name()} rw
			WHERE rw.advocate_id = %d
			ORDER BY rw.created_at DESC
			LIMIT %d OFFSET %d
		";

		return $this->query( $sql, [ $member_id, $limit, $offset ] );
	}

	/**
	 * Count total rewards activity entries for a member.
	 *
	 * Used for pagination to determine if there are more items to load.
	 *
	 * @since 1.2.0
	 *
	 * @param int $member_id Member ID.
	 *
	 * @return int Total count of reward activity entries.
	 */
	public function count_rewards_activity( int $member_id ): int {
		$sql = "
			SELECT COUNT(*) as count
			FROM {$this->rewards_table->get_prefixed_table_name()} rw
			WHERE rw.advocate_id = %d
		";

		$result = $this->get_var( $sql, [ $member_id ] );

		return (int) ( $result ?? 0 );
	}

	// ========================================================================
	// Pending Points
	// ========================================================================

	/**
	 * Get pending points for a member from incomplete orders.
	 *
	 * Calculates pending points from orders that are linked to referrals/rewards
	 * but haven't been completed yet. These are points that will be earned
	 * once the order status changes to 'completed'.
	 *
	 * @since 1.2.0
	 *
	 * @param int $member_id Member ID.
	 *
	 * @return int Total pending points from incomplete orders.
	 */
	public function get_pending_points_for_member( int $member_id ): int {
		// Get member instance.
		$member = get_member( $member_id );
		if ( ! $member ) {
			rewardswp_log_error(
				'Unable to retrieve member for pending points calculation',
				[ 'member_id' => $member_id ]
			);
			return 0;
		}

		// Get active integration source.
		$integration_source = get_active_integration_source();
		if ( empty( $integration_source ) ) {
			rewardswp_log_error(
				'No active integration source found for pending points calculation',
				[ 'member_id' => $member_id ]
			);
			return 0;
		}

		// Get integration instance to access order methods.
		$integration = $this->integrations_factory->get_integration( $integration_source );
		if ( ! $integration instanceof IntegrationInterface ) {
			rewardswp_log_error(
				'Integration instance is not valid for pending points calculation',
				[
					'member_id'          => $member_id,
					'integration_source' => $integration_source,
				]
			);
			return 0;
		}

		// Check if integration has the method (WooCommerce only for now).
		if ( ! method_exists( $integration, 'get_incomplete_orders_for_member' ) ) {
			rewardswp_log_error(
				'Integration does not support get_incomplete_orders_for_member method',
				[
					'member_id'          => $member_id,
					'integration_source' => $integration_source,
				]
			);
			return 0;
		}

		// Get incomplete orders for this member.
		$incomplete_orders = $integration->get_incomplete_orders_for_member( $member );

		if ( empty( $incomplete_orders ) ) {
			// This is expected when member has no incomplete orders, so no logging needed.
			return 0;
		}

		// Sum up expected points from all incomplete orders.
		$total_pending_points = 0;
		foreach ( $incomplete_orders as $order_data ) {
			$total_pending_points += (int) ( $order_data['expected_points'] ?? 0 );
		}

		return $total_pending_points;
	}
}
