<?php
/**
 * Points helper functions.
 *
 * @package Awesomemotive\Rewardswp\Helpers
 * @since 1.0.0
 */

namespace Awesomemotive\Rewardswp\Helpers;

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

/**
 * Default points redemption rate.
 * 100 points = $1 when redeeming for discounts.
 *
 * @since 1.1.0
 */
const POINTS_REDEMPTION_RATE = 100;

use Awesomemotive\Rewardswp\Models\Point;
use Awesomemotive\Rewardswp\Services\PointsService;
use Exception;
use WP_Error;

/**
 * Create a points entry.
 *
 * @since 1.0.1
 *
 * @param array $args           Array of arguments for creating a points entry.
 * @param bool  $notify         Optional. Whether to send notification email. Default true.
 * @param bool  $update_balance Optional. Whether to update member balance automatically. Default true.
 *
 * @return Point|WP_Error Point object if successful, WP_Error object on failure.
 */
function create_point_entry( array $args, bool $notify = true, bool $update_balance = true ) {
	try {
		return am_rewardswp()->get_container()->get( PointsService::class )->create_points_entry( $args, $notify, $update_balance );
	} catch ( Exception $e ) {
		return new WP_Error( 'points_creation_failed', $e->getMessage() );
	}
}

/**
 * Update an existing points entry.
 *
 * @since 1.2.0
 *
 * @param array $args           Array of arguments for updating a points entry.
 * @param bool  $notify         Optional. Whether to send notification email. Default true.
 * @param bool  $update_balance Optional. Whether to update member balance automatically. Default true.
 *
 * @return Point|WP_Error Point object if successful, WP_Error object on failure.
 */
function update_point_entry( array $args, bool $notify = true, bool $update_balance = true ) {
	try {
		return am_rewardswp()->get_container()->get( PointsService::class )->update_points_entry( $args, $notify, $update_balance );
	} catch ( Exception $e ) {
		return new WP_Error( 'points_update_failed', $e->getMessage() );
	}
}

/**
 * Get points entry by ID.
 *
 * @since 1.0.1
 *
 * @param int $point_id Point entry ID.
 *
 * @return Point|null Point object if found, null otherwise.
 */
function get_point_entry( int $point_id ): ?Point {
	try {
		$point_entry = am_rewardswp()->get_container()->make( Point::class );
		$point_entry->load( $point_id );

		return $point_entry;
	} catch ( Exception $e ) {
		return null;
	}
}

/**
 * Get points entries.
 *
 * @since 1.0.1
 *
 * @param array $args     Query arguments.
 * @param int   $per_page Number of items per page.
 * @param int   $page     Current page number.
 *
 * @return Point[] Array of Point objects.
 */
function get_points_entries( array $args = [], int $per_page = 20, int $page = 1 ): array {
	try {
		return am_rewardswp()->get_container()->get( PointsService::class )->get_points_entries( $args, $per_page, $page );
	} catch ( Exception $e ) {
		return [];
	}
}

/**
 * Count points entries.
 *
 * @since 1.0.1
 *
 * @param array $args Query arguments.
 *
 * @return int Number of points entries.
 */
function count_points_entries( array $args = [] ): int {
	try {
		return am_rewardswp()->get_container()->get( PointsService::class )->count_points_entries( $args );
	} catch ( Exception $e ) {
		return 0;
	}
}

/**
 * Get total points earned by a member.
 *
 * @since 1.0.0
 *
 * @param int $member_id The member ID.
 *
 * @return int
 */
function get_member_points_earned( int $member_id ): int {
	try {
		$points_service = am_rewardswp()->get_container()->make( PointsService::class );
		return $points_service->get_member_points_earned( $member_id );
	} catch ( Exception $e ) {
		return 0;
	}
}

/**
 * Get total points spent by a member.
 *
 * @since 1.0.0
 *
 * @param int $member_id The member ID.
 *
 * @return int
 */
function get_member_points_spent( int $member_id ): int {
	try {
		$points_service = am_rewardswp()->get_container()->make( PointsService::class );
		return $points_service->get_member_points_spent( $member_id );
	} catch ( Exception $e ) {
		return 0;
	}
}

/**
 * Get total points redeemed by a member (excluding manual adjustments).
 *
 * @since 1.0.0
 *
 * @param int $member_id The member ID.
 *
 * @return int
 */
function get_member_points_redeemed( int $member_id ): int {
	try {
		$points_service = am_rewardswp()->get_container()->make( PointsService::class );
		return $points_service->get_member_points_redeemed( $member_id );
	} catch ( Exception $e ) {
		return 0;
	}
}

/**
 * Get total pending points for a member.
 *
 * @since 1.2.0
 *
 * @param int $member_id The member ID.
 *
 * @return int
 */
function get_member_pending_points( int $member_id ): int {
	try {
		$points_service = am_rewardswp()->get_container()->make( PointsService::class );
		return $points_service->get_pending_points_sum( $member_id );
	} catch ( Exception $e ) {
		return 0;
	}
}

/**
 * Get net points earned for a specific order.
 *
 * Calculates earned points minus any refund deductions for the order.
 * This is useful for displaying the correct points earned on order
 * confirmation/details pages, accounting for partial or full refunds.
 *
 * @since 1.1.0
 *
 * @param int $order_id  The order/payment ID.
 * @param int $member_id The member ID.
 *
 * @return int Net points earned (can be 0 if fully refunded).
 */
function get_net_points_for_order( int $order_id, int $member_id ): int {
	try {
		$points_service = am_rewardswp()->get_container()->make( PointsService::class );
		return $points_service->get_net_points_earned_for_order( $order_id, $member_id );
	} catch ( Exception $e ) {
		return 0;
	}
}

/**
 * Get activity type label.
 *
 * @since 1.0.0
 *
 * @param string $activity_type The activity type.
 *
 * @return string
 */
function get_activity_type_label( string $activity_type ): string {
	$activity_types = [
		'placed_order'    => esc_html__( 'Placed Order', 'rewardswp' ),
		'signup'          => esc_html__( 'Sign Up', 'rewardswp' ),
		'referral'        => esc_html__( 'Referral', 'rewardswp' ),
		'manual_addition' => esc_html__( 'Manual Addition', 'rewardswp' ),
		'manual_removal'  => esc_html__( 'Manual Removal', 'rewardswp' ),
		'points_spent'    => esc_html__( 'Points Spent', 'rewardswp' ),
	];

	return $activity_types[ $activity_type ] ?? ucwords( str_replace( '_', ' ', $activity_type ) );
}

/**
 * Get point status label.
 *
 * @since 1.0.0
 *
 * @param string $status The point status.
 *
 * @return string
 */
function get_point_status_label( string $status ): string {
	$statuses = [
		'pending'  => esc_html__( 'Pending', 'rewardswp' ),
		'approved' => esc_html__( 'Approved', 'rewardswp' ),
		'expired'  => esc_html__( 'Expired', 'rewardswp' ),
		'revoked'  => esc_html__( 'Revoked', 'rewardswp' ),
	];

	return $statuses[ $status ] ?? ucwords( $status );
}

/**
 * Delete all points and their associated metadata for a given member ID.
 *
 * @since 1.1.0
 *
 * @param int $member_id The ID of the member.
 * @return bool True if all deletions were successful or if no points/metadata existed, false on any error.
 */
function delete_points_by_member_id( int $member_id ): bool {
	try {
		/**
		 * Points service instance.
		 *
		 * @var PointsService $points_service
		 */
		$points_service = am_rewardswp()->get_container()->get( PointsService::class );
		return $points_service->delete_by_member_id( $member_id );
	} catch ( Exception $e ) {
		// Optionally log the error: error_log( $e->getMessage() );
		return false;
	}
}

/**
 * Get points currency singular label.
 *
 * Returns the singular form of the points currency.
 * In Lite version, always returns 'Point'.
 * In Pro version, returns the customized value from settings.
 *
 * @since 1.1.0
 *
 * @return string The singular currency label (e.g., "Point" or "Coin").
 */
function get_points_currency_singular(): string {
	try {
		return am_rewardswp()->get_container()
			->get( PointsService::class )
			->get_currency_singular();
	} catch ( Exception $e ) {
		return __( 'Point', 'rewardswp' ); // Fallback if container fails.
	}
}

/**
 * Get points currency plural label.
 *
 * Returns the plural form of the points currency.
 * In Lite version, always returns 'Points'.
 * In Pro version, returns the customized value from settings.
 *
 * @since 1.1.0
 *
 * @return string The plural currency label (e.g., "Points" or "Coins").
 */
function get_points_currency_plural(): string {
	try {
		return am_rewardswp()->get_container()
			->get( PointsService::class )
			->get_currency_plural();
	} catch ( Exception $e ) {
		return __( 'Points', 'rewardswp' ); // Fallback if container fails.
	}
}

/**
 * Format points with the appropriate singular or plural label.
 *
 * Returns the points value with the correct currency label based on quantity.
 * Uses singular label for 1, plural for all other values (including 0).
 *
 * @since 1.1.0
 *
 * @param int $points The number of points to format.
 *
 * @return string Formatted points with label (e.g., "1 Point" or "5 Points").
 */
function format_points_with_label( $points ): string {
	$points = absint( $points );

	$singular = get_points_currency_singular();
	$plural   = get_points_currency_plural();

	$label = ( 1 === $points ) ? $singular : $plural;

	return number_format_i18n( $points ) . ' ' . $label;
}
