<?php
namespace RadiusTheme\RadiusBooking\Repositories;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly
}
use RadiusTheme\RadiusBooking\Models\User;
use RadiusTheme\RadiusBooking\Abstracts\BaseRepository;
use RadiusTheme\RadiusBooking\Models\UserProfile;

/**
 * Class UserProfileRepository
 *
 * Handles data operations for the UserProfile model.
 */
class UserProfileRepository extends BaseRepository {

	/**
	 * The model class managed by this repository.
	 *
	 * @var string
	 */
	protected string $model = UserProfile::class;


	/**
	 * Creates a new profile using the provided data.
	 *
	 * @param array $data An associative array containing profile details to be saved.
	 *
	 * @return mixed The created profile object or data, or null if creation fails.
	 */
	public function createProfile( $data ) {
		return $this->create( $data );
	}

	/**
	 * Retrieves the profile associated with the given profile identifier.
	 *
	 * @param int|string $profileId The unique identifier of the profile to retrieve.
	 *
	 * @return mixed The profile object if found, or null if no profile is found with the given identifier.
	 */
	public function findProfileById( $profileId ) {
		return $this->find( $profileId );
	}

	/**
	 * Retrieves a user profile based on the provided email address.
	 *
	 * @param string $email The email address of the user whose profile needs to be found.
	 *
	 * @return mixed The profile data matching the email address if found, or null if no profile exists.
	 */
	public function findProfileByEmail( $email ) {
		return $this->findBy( array( 'email' => $email ) );
	}

	/**
	 * Updates the profile identified by the given profile ID with the provided data.
	 *
	 * @param int|string $profileId The unique identifier of the profile to be updated.
	 * @param array $data An associative array containing the data to update the profile with.
	 *
	 * @return mixed The result of the update operation if the profile exists, or null if no profile is found.
	 */
	public function updateProfileById( $profileId, $data ) {
		$profile = $this->find( $profileId );
		if ( $profile ) {
			return $this->update( $profile->id, $data );
		}
		return null;
	}
	// update profile by email

	/**
	 * Updates the profile of a user identified by their email address with the given data.
	 *
	 * @param string $email The email address of the user whose profile needs to be updated.
	 * @param array $data An associative array containing the profile data to update.
	 *
	 * @return mixed The result of the update operation if the profile exists, or null if no profile is found.
	 */
	public function updateProfileByEmail( $email, $data ) {
		$profile = $this->findBy( array( 'email' => $email ) );
		if ( $profile ) {
			return $this->update( $profile->id, $data );
		}
		return null;
	}

	/**
	 * Updates the profile of a user with the given data.
	 *
	 * @param int|string $userId The unique identifier of the user whose profile needs to be updated.
	 * @param array $data An associative array containing the profile data to update.
	 *
	 * @return mixed The result of the update operation if the profile exists, or null if no profile is found.
	 */
	public function updateProfile( $userId, $data ) {
		$profile = $this->findBy( array( 'user_id' => $userId ) );
		if ( $profile ) {
			return $this->update( $profile->id, $data );
		}
		return null;
	}

	/**
	 * Creates a new profile or updates an existing profile for a user with the provided data.
	 *
	 * @param int|string $userId The unique identifier of the user whose profile is being created or updated.
	 * @param array $data An associative array containing the profile data to create or update.
	 *
	 * @return mixed The result of the create or update operation, either newly created or updated profile details.
	 */
	public function createOrUpdateProfile( $userId, $data ) {
		$profile = $this->findBy( array( 'user_id' => $userId ) );
		if ( $profile ) {
			return $this->update( $profile->id, $data );
		} else {
			$data['user_id'] = $userId;
			return $this->create( $data );
		}
	}

	/**
	 * Finds a single record from the database based on the specified conditions.
	 *
	 * @param array $conditions An associative array where the keys represent column names and the values are the criteria to match.
	 *
	 * @return mixed The first record that matches the specified conditions, or null if no record is found.
	 */
	public function findBy( array $conditions ) {
		$query = $this->model::query();
		foreach ( $conditions as $key => $value ) {
			$query->where( $key, $value );
		}
		return $query->first();
	}

	/**
	 * Generates a WordPress-compatible username based on the provided email address.
	 *
	 * @param string $email The email address used to generate the username.
	 *
	 * @return string A unique, sanitized username derived from the email address.
	 */
	public function getWPUserName( $email ) {
		// Extract the username from the email
		$username = sanitize_user( current( explode( '@', $email ) ) );
		// Ensure the username is unique
		if ( ! username_exists( $username ) ) {
			return $username;
		} else {
			// If the username already exists, append a number to make it unique
			$counter = 1;
			while ( username_exists( $username . $counter ) ) {
				++$counter;
			}
			return $username . $counter;
		}
	}

	/**
	 * Creates a new WordPress user or returns the ID of an existing user if already present.
	 *
	 * @param array $userData An associative array containing user data including:
	 *                        - 'email' (string): The user's email address.
	 *                        - 'username' (string): The user's username.
	 *                        - 'password' (string): The password for the user account.
	 *                        - 'first_name' (string, optional): The user's first name.
	 *                        - 'last_name' (string, optional): The user's last name.
	 *                        - 'role' (string): The role assigned to the user.
	 *
	 * @return int The ID of the created or existing user.
	 * @throws \Exception If the user creation process fails.
	 */
	public function createWpUser( $userData ) {
		$email    = $userData['email'];
		$username = $userData['username'];

		// Check if user already exists by email or username
		$existingUserId = email_exists( $email );
		if ( ! $existingUserId ) {
			$existingUserId = username_exists( $username );
		}

		if ( $existingUserId ) {
			return $existingUserId; // user already exists, return the ID
		}

		// Create new user
		$wp_user_data = array(
			'user_login' => $username,
			'user_pass'  => $userData['password'],
			'user_email' => $email,
			'first_name' => $userData['first_name'] ?? '',
			'last_name'  => $userData['last_name'] ?? '',
			'role'       => $userData['role'],
		);
		if(!empty($userData['_rtcl_user_type'])) {
			$wp_user_data['meta_input'] = [
				'_rtcl_user_type' => $userData['_rtcl_user_type'],
			];
		}

		$wp_user_id = wp_insert_user( $wp_user_data );

		if ( is_wp_error( $wp_user_id ) ) {
			throw new \Exception( 'Failed to create WordPress user: ' . $wp_user_id->get_error_message() ); //phpcs:ignore
		}

		return $wp_user_id;
	}

	/**
	 * Sets the user role based on the user's type.
	 *
	 * @param object $user The user object, which must have methods to retrieve its ID and type.
	 *
	 * @return string|null Returns the user role string based on the user's type if an ID is present, or null if no ID is found.
	 */
	public function setUserRole( $user ) {
		if ( $user->get_id() ) {
			$user_type = $user->get_type();
			return 'radius_booking_' . $user_type;
		}
	}


	/**
	 * Creates or updates a WordPress user profile and sends a new user notification.
	 *
	 * @param int $id The ID of the user.
	 * @param array $data An associative array of user data used for creating or updating the profile.
	 *
	 * @return void
	 */
	public function createOrUpdateWpUser( $id, $data ) {
		$this->createOrUpdateProfile( $id, $data );
		wp_new_user_notification( $data['wp_user_id'], null, 'user' );
	}
	// You can add custom repository methods here

	/**
	 * Retrieves a user profile based on the provided email address.
	 *
	 * @param string $email The email address of the user whose profile needs to be found.
	 *
	 * @return mixed The profile data matching the email address if found, or null if no profile exists.
	 */
	public function findProfileByWPId( $wp_user_id ) {
		return $this->findBy( array( 'wp_user_id' => $wp_user_id ) );
	}
}
