<?php

namespace WC_Affiliate\Models;

defined( 'ABSPATH' ) || exit;

use WC_Affiliate\Traits\Validation;
use WP_Error;

/**
 * User Model Class
 *
 * Handles user-related operations and data management for the affiliate system.
 * Provides methods for user validation, data retrieval, and user-specific operations.
 *
 * @since   1.0.0
 * @package WC_Affiliate\Models
 */
class User {

	use Validation;

	/**
	 * User ID.
	 *
	 * @var int
	 */
	protected int $id = 0;

	/**
	 * User email.
	 *
	 * @var string
	 */
	protected string $email = '';

	/**
	 * User name.
	 *
	 * @var string
	 */
	protected string $name = '';

	/**
	 * User role.
	 *
	 * @var string
	 */
	protected string $role = '';

	/**
	 * User password.
	 *
	 * @var string
	 */
	protected string $password;

	/**
	 * Constructor for the User class.
	 *
	 * @param int|null $id Optional. User ID to initialize the class with. Default null.
	 */
	/**
	 * User model constructor.
	 *
	 * @param int|null $id User ID to load. If null, creates empty user object.
	 */
	public function __construct( ?int $id = null ) {
		if ( $id ) {
			$user = get_userdata( $id );
			if ( $user instanceof \WP_User && $user->exists() ) {
				$this->id    = $id;
				$this->email = $user->user_email;
				$this->name  = $user->display_name;

				if ( in_array( 'affiliate', $user->roles, true ) ) {
					$this->role = 'affiliate';
				} else {
					$this->role = $user->roles[0] ?? 'subscriber';
				}
			}
		}
	}

	/**
	 * Convert user data to array format.
	 *
	 * @return array User data array in API format.
	 */
	public function to_array(): array {
		if ( ! $this->id ) {
			return array();
		}

		$user = \get_userdata( $this->id );

		if ( ! $user instanceof \WP_User ) {
			return array();
		}

		$data = array(
			'id'              => $this->id,
			'user_email'      => $user->user_email,
			'user_login'      => $user->user_login,
			'first_name'      => $user->first_name,
			'last_name'       => $user->last_name,
			'display_name'    => $user->display_name,
			'user_registered' => $user->user_registered,
			'avatar_url'      => $this->get_meta( '_wc_affiliate_avatar' ),
			'city'            => $this->get_meta( '_wc_affiliate_city' ),
			'state'           => $this->get_meta( '_wc_affiliate_state' ),
			'country'         => $this->get_meta( '_wc_affiliate_country' ),
			'payout_method'   => $this->get_meta( '_wc_affiliate_payout_method' ),
			'account_info'    => $this->get_meta( '_wc_affiliate_mannual_payment' ),
			'wp_avatar_url'   => \get_avatar_url( $this->email, array( 'size' => 96 ) ),
		);

		return apply_filters( 'wc_affiliate_user_data', $data, $this->id );
	}

	/**
	 * Get user ID.
	 *
	 * @return int
	 */
	public function get_id(): int {
		return $this->id;
	}

	/**
	 * Get user email.
	 *
	 * @return string
	 */
	public function get_email(): string {
		return $this->email;
	}

	/**
	 * Get user name.
	 *
	 * @return string
	 */
	public function get_name(): string {
		return $this->name;
	}

	/**
	 * Get user role.
	 *
	 * @return string
	 */
	public function get_role(): string {
		return $this->role;
	}

	/**
	 * Set user email.
	 *
	 * @param string $email The email address.
	 */
	public function set_email( string $email ) {
		$this->email = $email;
	}

	/**
	 * Set user name.
	 *
	 * @param string $name The user name.
	 */
	public function set_name( string $name ) {
		$this->name = $name;
	}

	/**
	 * Set user role.
	 *
	 * @param string $role The user role.
	 */
	public function set_role( string $role ) {
		$this->role = $role;
	}

	/**
	 * Save user data.
	 *
	 * @return bool
	 */
	public function save(): bool {
		// Create the new user.
		$userdata = array(
			'user_email'   => $this->email,
			'display_name' => $this->name,
			'role'         => $this->role,
		);

		if ( $this->id ) {
			// Update existing user.
			$userdata['ID'] = $this->id;

			$user_id = \wp_update_user( $userdata );
		} else {
			// Create the new user.
			$userdata['user_login'] = $this->email;
			$userdata['user_pass']  = $this->password;

			$user_id = self::create_user( $userdata );

			if ( ! is_wp_error( $user_id ) ) {
				$this->id = $user_id;
			}
		}

		return ! is_wp_error( $user_id );
	}

	/**
	 * Delete user.
	 *
	 * @return bool
	 */
	public function delete(): bool {
		if ( ! function_exists( 'wp_delete_user' ) ) {
			require_once ABSPATH . 'wp-admin/includes/user.php';
		}

		return \wp_delete_user( $this->id );
	}

	/**
	 * Add user meta data.
	 *
	 * @param string $key   Key for the meta data.
	 * @param mixed  $value Value for the meta data.
	 *
	 * @return bool
	 */
	public function add_meta( string $key, $value ): bool {
		return \add_user_meta( $this->id, $key, $value );
	}

	/**
	 * Get user meta data.
	 *
	 * @param string $key    Key for the meta data.
	 * @param bool   $single Whether to return a single value.
	 *
	 * @return mixed
	 */
	public function get_meta( string $key, bool $single = true ) {
		return get_user_meta( $this->id, $key, $single );
	}

	/**
	 * Update user meta data.
	 *
	 * @param string $key   Key for the meta data.
	 * @param mixed  $value Value for the meta data.
	 *
	 * @return bool
	 */
	public function update_meta( string $key, $value ): bool {
		// Allow filtering meta value before update.
		$value = apply_filters( 'wc_affiliate_user_update_meta_value', $value, $key, $this->id );

		// Fire action before meta update.
		do_action( 'wc_affiliate_user_before_update_meta', $this->id, $key, $value );

		$result = update_user_meta( $this->id, $key, $value );

		if ( $result ) {
			// Fire action after meta update.
			do_action( 'wc_affiliate_user_after_update_meta', $this->id, $key, $value );
		}

		return $result;
	}

	/**
	 * Delete user meta data.
	 *
	 * @param string $key Key for the meta data.
	 *
	 * @return bool
	 */
	public function delete_meta( string $key ): bool {
		// Fire action before meta deletion.
		do_action( 'wc_affiliate_user_before_delete_meta', $this->id, $key );

		$result = delete_user_meta( $this->id, $key );

		if ( $result ) {
			// Fire action after meta deletion.
			do_action( 'wc_affiliate_user_after_delete_meta', $this->id, $key );
		}

		return $result;
	}

	/**
	 * @param string $key
	 *
	 * @return bool
	 */
	public function meta_exists( string $key ): bool {
		return metadata_exists( 'user', $this->id, $key );
	}

	/**
	 * Create a new user.
	 *
	 * @param array $args Array of user data. Must include 'email' and 'name'. Optional 'role' and 'password'.
	 *
	 * @return bool
	 */
	public function create( array $args ): bool {
		if ( empty( $args['email'] ) || empty( $args['name'] ) ) {
			return false;
		}

		$this->email    = $args['email'];
		$this->name     = $args['name'];
		$this->role     = $args['role'] ?? $this->role;
		$this->password = $args['password'] ?? wp_generate_password();

		return $this->save();
	}

	/**
	 * Create a new WordPress user.
	 *
	 * Central method for creating WordPress users with proper validation and error handling.
	 *
	 * @since 1.0.0
	 *
	 * @param array $user_data User data array (user_login, user_pass, user_email, first_name, last_name, display_name, role).
	 *
	 * @return int|WP_Error User ID on success, WP_Error on failure.
	 */
	public static function create_user( array $user_data ) {
		// Allow plugins to prevent user creation.
		$can_create = apply_filters( 'wc_affiliate_can_create_user', true, $user_data );
		if ( ! $can_create ) {
			return new WP_Error( 'creation_prevented', __( 'User creation prevented by filter', 'wc-affiliate' ) );
		}

		// Fire action before user creation.
		do_action( 'wc_affiliate_before_create_user', $user_data );

		// Validate required fields.
		$required_fields = array( 'user_login', 'user_pass', 'user_email' );
		foreach ( $required_fields as $field ) {
			if ( empty( $user_data[ $field ] ) ) {
				/* translators: %s: field name */
				return new \WP_Error( 'missing_field', sprintf( __( 'Required field "%s" is missing', 'wc-affiliate' ), $field ) );
			}
		}

		// Validate email format.
		if ( ! is_email( $user_data['user_email'] ) ) {
			return new \WP_Error( 'invalid_email', __( 'Invalid email address', 'wc-affiliate' ) );
		}

		// Check for existing users.
		if ( email_exists( $user_data['user_email'] ) ) {
			return new \WP_Error( 'email_exists', __( 'Email address already exists', 'wc-affiliate' ) );
		}

		if ( username_exists( $user_data['user_login'] ) ) {
			return new \WP_Error( 'username_exists', __( 'Username already exists', 'wc-affiliate' ) );
		}

		// Set default role if not provided.
		if ( ! isset( $user_data['role'] ) ) {
			$user_data['role'] = 'affiliate';
		}

		$user_data = apply_filters( 'wc_affiliate_create_user_data', $user_data );

		// Create the user.
		$user_id = wp_insert_user( $user_data );

		if ( is_wp_error( $user_id ) ) {
			return $user_id;
		}

		// Ensure the role is properly assigned.
		$user = new \WP_User( $user_id );
		if ( ! empty( $user_data['role'] ) && ! in_array( $user_data['role'], $user->roles, true ) ) {
			$user->set_role( $user_data['role'] );
		}

		// Fire action after user creation.
		do_action( 'wc_affiliate_after_create_user', $user_id, $user_data );

		return $user_id;
	}
}
