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

namespace Awesomemotive\Rewardswp\Helpers;

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

use Awesomemotive\Rewardswp\Models\Member;
use Awesomemotive\Rewardswp\Services\GuestMemberService;
use Awesomemotive\Rewardswp\Services\MembersService;
use Exception;
use WP_Error;

/**
 * Returns if the current logged-in user is a member.
 *
 * @since 1.0.0
 *
 * @return bool Whether the current user is a member.
 */
function is_current_user_a_member(): bool {
	return (bool) get_current_member();
}

/**
 * Check if the current user is logged in but not a member.
 *
 * @since 1.1.0
 *
 * @return bool Whether the current user is logged in but not a member.
 */
function is_current_user_logged_in_but_not_member(): bool {
	return is_user_logged_in() && ! is_current_user_a_member();
}

/**
 * Check if the current member is a guest member.
 *
 * @since 1.0.1
 *
 * @return bool Whether the current member is a guest member.
 */
function is_current_member_a_guest(): bool {
	$member = get_current_member();

	if ( is_null( $member ) ) {
		return false;
	}

	return 'guest' === $member->get_status();
}

/**
 * Check if guest participation is enabled in the settings.
 *
 * This checks both:
 * 1. If the admin has selected "all_customers" in settings
 * 2. If guest checkout is actually enabled in WooCommerce or EDD
 *
 * @since 1.1.0
 *
 * @return bool Whether guest members can participate in the rewards program.
 */
function is_guest_participation_enabled(): bool {
	$guest_participation = get_option_value( \Awesomemotive\Rewardswp\Settings\Options\General\ProgramParticipantsOption::class );

	// First check if admin has selected "all_customers".
	if ( 'all_customers' !== $guest_participation ) {
		return false;
	}

	// Then check if guest checkout is actually enabled in WooCommerce or EDD.
	return is_guest_checkout_enabled();
}

/**
 * Check if guest checkout is enabled in WooCommerce or Easy Digital Downloads.
 *
 * @since 1.1.0
 *
 * @return bool True if guest checkout is enabled in at least one platform, false otherwise.
 */
function is_guest_checkout_enabled(): bool {
	// Check WooCommerce.
	if ( class_exists( 'WooCommerce' ) ) {
		$wc_guest_checkout = get_option( 'woocommerce_enable_guest_checkout', 'no' );
		if ( 'yes' === $wc_guest_checkout ) {
			return true;
		}
	}

	// Check Easy Digital Downloads.
	if ( function_exists( 'edd_get_option' ) ) {
		$edd_guest_checkout = edd_get_option( 'logged_in_only', false );
		// EDD's logged_in_only is the opposite - if false, guests can checkout.
		if ( ! $edd_guest_checkout ) {
			return true;
		}
	}

	return false;
}

/**
 * Retrieve the current member ID.
 *
 * @since 1.0.0
 *
 * @return int
 */
function get_current_user_member_id(): int {
	$member = get_current_member();

	return is_null( $member ) ? 0 : $member->id;
}

/**
 * Retrieve the member object for the current user.
 *
 * @since 1.0.0
 *
 * @return Member|null
 */
function get_current_member(): ?Member {
	// Allow overriding the current member via filter (e.g., admin preview).
	$override_member = apply_filters( 'rewardswp_current_member_override', null );

	// If filter returns false, explicitly force "not logged in" state (e.g., admin preview logged_out).
	if ( false === $override_member ) {
		return null;
	}

	if ( $override_member instanceof Member ) {
		return $override_member;
	}

	if ( is_user_logged_in() ) {
		return get_member_by( 'user_id', get_current_user_id() );
	}

	try {
		$guest_member_service = am_rewardswp()->get_container()->make( GuestMemberService::class );

		return $guest_member_service->get_member_by_token();
	} catch ( Exception $e ) {
		return null;
	}
}

/**
 * Retrieve a list of members matching the given criteria.
 *
 * @since 1.4.0
 *
 * @param array $where    Optional. Array of query conditions.
 * @param int   $per_page Optional. Number of members per page. Default 10.
 * @param int   $page     Optional. Page number. Default 1.
 *
 * @return Member[] Array of Member objects.
 */
function get_members( array $where = [], int $per_page = 10, int $page = 1 ): array {
	try {
		$service = am_rewardswp()->get_container()->make( MembersService::class );

		return $service->get_members( $where, $per_page, $page );
	} catch ( Exception $e ) {
		return [];
	}
}

/**
 * Retrieves an member based on a unique column and value.
 *
 * @param string $source The column to search by (e.g., 'id', 'user_id', 'email', 'referral_code', 'token').
 * @param mixed  $value  The value to search for.
 *
 * @return Member|null The Member instance or null in case of failure.
 */
function get_member_by( string $source, $value ): ?Member {
	try {
		$member = am_rewardswp()->get_container()->make( Member::class );

		// Attempt to load the member by the specified column and value.
		if ( ! $member->load_by( sanitize_text_field( $source ), sanitize_text_field( $value ) ) ) {
			return null; // Return null if load_by fails to find the member.
		}

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

/**
 * Retrieves an member by id.
 *
 * @param int $member_id The member ID.
 *
 * @return Member|null The Member instance or null in case of failure.
 */
function get_member( int $member_id ): ?Member {
	return get_member_by( 'id', $member_id );
}

/**
 * Create a new member.
 *
 * @param array $args The arguments for creating the member (e.g., 'user_id', 'email', etc.).
 *
 * @return Member|WP_Error The created member object or a WP_Error on failure.
 */
function create_member( array $args ) {
	$args = wp_parse_args(
		$args,
		[
			'user_id'             => null,
			'email'               => '',
			'first_name'          => '',
			'last_name'           => '',
			'referral_code'       => '',
			'token'               => null,
			'total_referrals'     => 0,
			'completed_referrals' => 0,
			'status'              => 'active',
		]
	);

	try {
		$member = am_rewardswp()->get_container()->make( Member::class );

		// If user_id is provided, and email is missing, get the email from the WP user.
		if ( ! empty( $args['user_id'] ) && empty( $args['email'] ) ) {
			$user = get_user_by( 'id', $args['user_id'] );
			if ( $user ) {
				$args['email'] = $user->user_email;
			} else {
				return new WP_Error( 'invalid_user_id', __( 'Invalid user ID provided.', 'rewardswp' ) );
			}
		}

		// Populate the object.
		$member->hydrate( $args );

		// Attempt to create the member.
		$member->create();

		return $member;
	} catch ( Exception $e ) {
		return new WP_Error( 'general_error', $e->getMessage() );
	}
}

/**
 * Update an existing member.
 *
 * @param int   $member_id The ID of the member to update.
 * @param array $args        The arguments for updating the member (e.g., 'user_id', 'email', etc.).
 *
 * @return Member|WP_Error The updated member object or a WP_Error on failure.
 */
function update_member( int $member_id, array $args ) {
	$args = array_filter(
		wp_parse_args(
			$args,
			[
				'user_id'             => null,
				'email'               => '',
				'first_name'          => '',
				'last_name'           => '',
				'referral_code'       => '',
				'token'               => '',
				'total_referrals'     => null,
				'completed_referrals' => null,
				'status'              => null,
			]
		)
	);

	try {
		$member = am_rewardswp()->get_container()->make( Member::class );

		// Load the member by ID.
		if ( ! $member->load_by( 'id', $member_id ) ) {
			return new WP_Error( 'not_found', __( 'Member not found.', 'rewardswp' ) );
		}

		// Capture old status before hydrate.
		$old_status = $member->get_status();

		// Populate the object.
		$member->hydrate( $args );

		// Attempt to update the member.
		$member->update();

		// Fire hook if status actually changed.
		$new_status = $member->get_status();
		if ( $old_status !== $new_status ) {
			/**
			 * Fires when a member's status changes.
			 *
			 * @since 1.3.0
			 *
			 * @param Member $member     The updated member object.
			 * @param string $old_status The previous status.
			 * @param string $new_status The new status.
			 */
			do_action( 'rewardswp_member_status_changed', $member, $old_status, $new_status );
		}

		return $member;
	} catch ( Exception $e ) {
		return new WP_Error( 'general_error', $e->getMessage() );
	}
}

/**
 * Deletes an member by ID.
 *
 * @param int $member_id Member ID.
 * @return true|WP_Error True on success, or WP_Error on failure.
 */
function delete_member( int $member_id ) {
	try {
		$member = am_rewardswp()->get_container()->make( Member::class );

		// Try to load the member by the ID.
		if ( ! $member->load_by( 'id', $member_id ) ) {
			return new WP_Error( 'not_found', __( 'Member not found.', 'rewardswp' ) );
		}

		// Attempt to delete the member.
		$member->delete();

		return true;
	} catch ( Exception $e ) {
		return new WP_Error( 'general_error', $e->getMessage() );
	}
}

/**
 * Get formatted HTML for the member status badge.
 *
 * @since 1.0.0
 *
 * @param Member $member The member object.
 *
 * @return string The HTML for the status badge.
 */
function get_member_status_badge_html( Member $member ): string {
	$status_classes = [
		'active'   => 'bg-green-50 text-green-700 ring-green-600/20',
		'inactive' => 'bg-gray-50 text-gray-600 ring-gray-500/20',
		'blocked'  => 'bg-red-50 text-red-700 ring-red-600/20',
		'excluded' => 'bg-gray-50 text-gray-600 ring-gray-500/20',
		'pending'  => 'bg-yellow-50 text-yellow-700 ring-yellow-600/20',
		'guest'    => 'bg-blue-50 text-blue-700 ring-blue-600/20',
	];
	$current_status = $member->get_status(); // Use getter to ensure consistent status retrieval (e.g., for guests)
	$status_class   = $status_classes[ $current_status ] ?? $status_classes['inactive'];
	$status_label   = $member->statuses[ $current_status ] ?? ucfirst( $current_status );

	return sprintf(
		'<span class="inline-flex items-center rounded-md px-2 py-1 text-xs font-medium ring-1 ring-inset %s">%s</span>',
		esc_attr( $status_class ),
		esc_html( $status_label )
	);
}
