<?php

namespace WC_Affiliate\Traits;

use WP_Error;

defined( 'ABSPATH' ) || exit;

/**
 * Trait Validation
 *
 * This trait provides common validation methods and standardized error handling used across models.
 *
 * @package WC_Affiliate
 */
trait Validation {

	/**
	 * Validate user ID
	 *
	 * @param mixed $user_id The user ID to validate.
	 *
	 * @return bool|WP_Error True if valid, WP_Error object if invalid.
	 */
	protected function validate_user_id( $user_id ) {
		if ( empty( $user_id ) || ! is_numeric( $user_id ) || $user_id <= 0 ) {
			return $this->create_invalid_data_error( 'user_id', __( 'Valid user ID is required', 'wc-affiliate' ) );
		}

		if ( ! get_userdata( $user_id ) ) {
			return $this->create_invalid_data_error( 'user_id', __( 'User does not exist', 'wc-affiliate' ) );
		}

		return true;
	}

	/**
	 * Validate affiliate ID
	 *
	 * @param mixed $affiliate_id The affiliate ID to validate.
	 * @param array $errors       Array to store validation errors.
	 *
	 * @return bool True if valid, false otherwise.
	 */
	protected function validate_affiliate_id( $affiliate_id, array &$errors ): bool {
		if ( empty( $affiliate_id ) || ! is_numeric( $affiliate_id ) || $affiliate_id <= 0 ) {
			$errors[] = __( 'Valid affiliate ID is required', 'wc-affiliate' );

			return false;
		}

		return true;
	}

	/**
	 * Validate amount
	 *
	 * @param mixed $amount     The amount to validate.
	 * @param array $errors     Array to store validation errors.
	 * @param bool  $allow_zero Whether to allow zero amounts.
	 *
	 * @return bool True if valid, false otherwise.
	 */
	protected function validate_amount( $amount, array &$errors, bool $allow_zero = false ): bool {
		if ( ! is_numeric( $amount ) ) {
			$errors[] = __( 'Amount must be a number', 'wc-affiliate' );

			return false;
		}

		$amount = (float) $amount;
		if ( ! $allow_zero && $amount <= 0 ) {
			$errors[] = __( 'Amount must be greater than zero', 'wc-affiliate' );

			return false;
		} elseif ( $allow_zero && $amount < 0 ) {
			$errors[] = __( 'Amount cannot be negative', 'wc-affiliate' );

			return false;
		}

		return true;
	}

	/**
	 * Validate status
	 *
	 * @param mixed $status         The status to validate.
	 * @param array $valid_statuses Array of valid status values.
	 * @param array $errors         Array to store validation errors.
	 *
	 * @return bool True if valid, false otherwise.
	 */
	protected function validate_status( $status, array $valid_statuses, array &$errors ): bool {
		if ( empty( $status ) ) {
			$errors[] = __( 'Status is required', 'wc-affiliate' );

			return false;
		}

		if ( ! in_array( $status, $valid_statuses, true ) ) {
			$errors[] = sprintf(
			/* translators: %s: list of valid statuses */
				__( 'Invalid status. Must be one of: %s', 'wc-affiliate' ),
				implode( ', ', $valid_statuses )
			);

			return false;
		}

		return true;
	}

	/**
	 * Validate email
	 *
	 * @param mixed $email    The email to validate.
	 * @param array $errors   Array to store validation errors.
	 * @param bool  $required Whether email is required.
	 *
	 * @return bool True if valid, false otherwise.
	 */
	protected function validate_email( $email, array &$errors, bool $required = true ): bool {
		if ( $required && empty( $email ) ) {
			$errors[] = __( 'Email is required', 'wc-affiliate' );

			return false;
		}

		if ( ! empty( $email ) && ! is_email( $email ) ) {
			$errors[] = __( 'Invalid email address', 'wc-affiliate' );

			return false;
		}

		return true;
	}

	/**
	 * Validate URL
	 *
	 * @param mixed $url      The URL to validate.
	 * @param array $errors   Array to store validation errors.
	 * @param bool  $required Whether URL is required.
	 *
	 * @return bool True if valid, false otherwise.
	 */
	protected function validate_url( $url, array &$errors, bool $required = true ): bool {
		if ( $required && empty( $url ) ) {
			$errors[] = __( 'URL is required', 'wc-affiliate' );

			return false;
		}

		if ( ! empty( $url ) && ! filter_var( $url, FILTER_VALIDATE_URL ) ) {
			$errors[] = __( 'Invalid URL format', 'wc-affiliate' );

			return false;
		}

		return true;
	}

	/**
	 * Validate order ID
	 *
	 * @param mixed $order_id The order ID to validate.
	 * @param array $errors   Array to store validation errors.
	 *
	 * @return bool True if valid, false otherwise.
	 */
	protected function validate_order_id( $order_id, array &$errors ): bool {
		if ( empty( $order_id ) || ! is_numeric( $order_id ) || $order_id <= 0 ) {
			$errors[] = __( 'Valid order ID is required', 'wc-affiliate' );

			return false;
		}

		return true;
	}

	/**
	 * Validate commission type
	 *
	 * @param mixed $type   The commission type to validate.
	 * @param array $errors Array to store validation errors.
	 *
	 * @return bool True if valid, false otherwise.
	 */
	protected function validate_commission_type( $type, array &$errors ): bool {
		$valid_types = array( 'fixed', 'percent' );
		if ( ! empty( $type ) && ! in_array( $type, $valid_types, true ) ) {
			$errors[] = __( 'Commission type must be either fixed or percent', 'wc-affiliate' );

			return false;
		}

		return true;
	}

	/**
	 * Validate percentage value
	 *
	 * @param mixed $percentage The percentage to validate.
	 * @param array $errors     Array to store validation errors.
	 * @param int   $max        Maximum allowed percentage.
	 *
	 * @return bool True if valid, false otherwise.
	 */
	protected function validate_percentage( $percentage, array &$errors, int $max = 100 ): bool {
		if ( ! is_numeric( $percentage ) ) {
			$errors[] = __( 'Percentage must be a number', 'wc-affiliate' );

			return false;
		}

		$percentage = (float) $percentage;
		if ( $percentage < 0 || $percentage > $max ) {
			$errors[] = sprintf(
			/* translators: %d: maximum percentage value */
				__( 'Percentage must be between 0 and %d', 'wc-affiliate' ),
				$max
			);

			return false;
		}

		return true;
	}

	/**
	 * Validate timestamp
	 *
	 * @param mixed $timestamp The timestamp to validate.
	 * @param array $errors    Array to store validation errors.
	 * @param bool  $required  Whether the timestamp is required.
	 */
	protected function validate_timestamp( $timestamp, &$errors, $required = false ): bool {
		if ( $required && empty( $timestamp ) ) {
			$errors[] = __( 'Timestamp is required', 'wc-affiliate' );

			return false;
		}

		if ( ! empty( $timestamp ) && ( ! is_numeric( $timestamp ) || $timestamp <= 0 ) ) {
			$errors[] = __( 'Invalid timestamp', 'wc-affiliate' );

			return false;
		}

		return true;
	}

	/**
	 * Validate IP address
	 *
	 * @param string $ip       The IP address to validate.
	 * @param array  $errors   Array to store validation errors.
	 * @param bool   $required Whether the IP address is required.
	 */
	protected function validate_ip( $ip, &$errors, $required = false ): bool {
		if ( $required && empty( $ip ) ) {
			$errors[] = __( 'IP address is required', 'wc-affiliate' );

			return false;
		}

		if ( ! empty( $ip ) && ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
			$errors[] = __( 'Invalid IP address', 'wc-affiliate' );

			return false;
		}

		return true;
	}

	/**
	 * Validate required fields
	 *
	 * @param array $data            The data array to validate.
	 * @param array $required_fields Array of required field names.
	 * @param array $errors          Array to store validation errors.
	 */
	protected function validate_required_fields( $data, $required_fields, &$errors ): bool {
		foreach ( $required_fields as $field ) {
			if ( ! isset( $data[ $field ] ) || empty( $data[ $field ] ) ) {
				$errors[] = sprintf(
				/* translators: %s: field name */
					__( 'Field %s is required', 'wc-affiliate' ),
					$field
				);
			}
		}

		return empty( $errors );
	}

	/**
	 * Create a standardized WP_Error object
	 *
	 * @param string $code    Error code.
	 * @param string $message Error message.
	 * @param array  $data    Additional error data.
	 * @param int    $status  HTTP status code for REST API responses.
	 *
	 * @return WP_Error Standardized error object.
	 */
	protected function create_error( string $code, string $message, array $data = array(), int $status = 400 ): WP_Error {
		$error_data = array_merge( $data, array( 'status' => $status ) );

		return new WP_Error( $code, $message, $error_data );
	}

	/**
	 * Create a validation error from error array
	 *
	 * @param array $errors Array of validation error messages.
	 *
	 * @return WP_Error Validation error object.
	 */
	protected function create_validation_error( array $errors ): WP_Error {
		return $this->create_error(
			'validation_failed',
			__( 'Validation failed', 'wc-affiliate' ),
			array( 'errors' => $errors )
		);
	}

	/**
	 * Create a not found error
	 *
	 * @param string $resource_type Type of resource that was not found.
	 *
	 * @return WP_Error Not found error object.
	 */
	protected function create_not_found_error( string $resource_type ): WP_Error {
		return $this->create_error(
			'not_found',
			sprintf(
			/* translators: %s: resource type */
				__( '%s not found', 'wc-affiliate' ),
				$resource_type
			),
			array(),
			404
		);
	}

	/**
	 * Create a permission denied error
	 *
	 * @param string $action Action that was denied.
	 *
	 * @return WP_Error Permission denied error object.
	 */
	protected function create_permission_error( string $action = '' ): WP_Error {
		$message = empty( $action )
			? __( 'Permission denied', 'wc-affiliate' )
			: sprintf(
			/* translators: %s: action name */
				__( 'Permission denied for action: %s', 'wc-affiliate' ),
				$action
			);

		return $this->create_error( 'permission_denied', $message, array(), 403 );
	}

	/**
	 * Create an invalid data error
	 *
	 * @param string $field  Field that contains invalid data.
	 * @param string $reason Reason why the data is invalid.
	 *
	 * @return WP_Error Invalid data error object.
	 */
	protected function create_invalid_data_error( string $field, string $reason ): WP_Error {
		return $this->create_error(
			'invalid_data',
			sprintf(
			/* translators: 1: field name, 2: reason */
				__( 'Invalid data for field %1$s: %2$s', 'wc-affiliate' ),
				$field,
				$reason
			)
		);
	}
}
