<?php
/**
 * Updated UserController with Fixed Nested Validation
 *
 * @package RadiusTheme\RadiusBooking\Controllers
 */

namespace RadiusTheme\RadiusBooking\Controllers;

use RadiusTheme\RadiusBooking\Core\Api\ApiResponse;
use RadiusTheme\RadiusBooking\Core\Api\Middleware\PermissionMiddleware;
use WP_REST_Request;
use WP_Error;
use RadiusTheme\RadiusBooking\Abstracts\BaseController;
use RadiusTheme\RadiusBooking\Core\Container\Container;
use RadiusTheme\RadiusBooking\Core\Api\Middleware\AuthMiddleware;
use RadiusTheme\RadiusBooking\Core\Api\Validation\Rules\EmailRule;
use RadiusTheme\RadiusBooking\Core\Api\Validation\Rules\RequiredRule;
use RadiusTheme\RadiusBooking\Core\Api\Validation\Rules\ArrayRule;
use RadiusTheme\RadiusBooking\Core\Api\Validation\Rules\NumericRule;
use RadiusTheme\RadiusBooking\Core\Api\Validation\Rules\StringRule;
use RadiusTheme\RadiusBooking\Core\Api\Validation\Rules\DateRule;
use RadiusTheme\RadiusBooking\Core\Api\Validation\Rules\TimeRule;
use RadiusTheme\RadiusBooking\Core\Api\Validation\Rules\BooleanRule;
use RadiusTheme\RadiusBooking\Core\Api\Validation\Rules\InRule;
use RadiusTheme\RadiusBooking\Core\Api\Validation\Rules\MinRule;
use RadiusTheme\RadiusBooking\Core\Api\Validation\Rules\MaxRule;
use RadiusTheme\RadiusBooking\Models\User;
use RadiusTheme\RadiusBooking\Models\UserProfile;
use RadiusTheme\RadiusBooking\Repositories\UserRepository;
use RadiusTheme\RadiusBooking\Resources\UserResource;
use RadiusTheme\RadiusBooking\Services\UserService;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly
}

/**
 * User controller
 */
class UserController extends BaseController {

	/**
	 * UserRepository instance
	 */
	public function __construct() {
		parent::__construct( Container::resolve( UserRepository::class ) );

		 $this->middleware[] = new AuthMiddleware();
		 $this->middleware[] = new PermissionMiddleware( 'manage_options' );
		add_action( 'radius_booking_item_created_user', array( $this, 'handleUserCreated' ), 10, 4 );
		add_action( 'radius_booking_item_updated_user', array( $this, 'handleUserUpdated' ), 10, 3 );

		add_filter( 'radius_booking_show_item_user', array( $this, 'showWithProfile' ), 10, 3 );
		add_filter( 'radius_booking_index_items_user', array( $this, 'showWithProfileIndex' ), 10, 2 );
		add_filter( 'radius_booking_created_item_user', array( $this, 'showAfterCreateOrUpdateUser' ), 10, 3 );
		add_filter( 'radius_booking_updated_item_user', array( $this, 'showAfterCreateOrUpdateUser' ), 10, 3 );
		add_filter( 'radius_booking_index_query_condition_user', array( $this, 'setCondition' ), 10, 3 );
	}

	/**
	 * Get resource type
	 *
	 * @return string
	 */
	protected function getResourceType(): string {
		return 'user';
	}

	/**
	 * Get validation rules based on request parameters and context
	 *
	 * @param object $request The request object containing parameters.
	 * @param string $context The context of the validation, such as 'create' or 'update'.
	 *
	 * @return array An array of validation rules structured by fields.
	 */
	protected function getValidationRules( $request, string $context = 'create' ): array {
		$rules = array(
			// Basic user fields
			'email'      => array( new RequiredRule(), new EmailRule() ),
			'user_type'  => array( new RequiredRule(), new InRule( array( 'employee', 'customer', 'admin' ) ) ),
			'first_name' => array( new RequiredRule(), new StringRule(), new MaxRule( 100 ) ),
			'last_name'  => array( new RequiredRule(), new StringRule(), new MaxRule( 100 ) ),
		);

		if ( $request->get_param( 'user_type' ) === 'customer' ) {
			return $rules;
		}
//		if ( $request->get_param( 'user_type' ) === 'employee' ) {
//			$rules['location_id'] = array( new RequiredRule() );
//		}
		if ( 'create' === $context ) {
			return $rules;
		}
		// Add employee-specific nested validation rules
		if ( 'update' === $context ) {
			$rules = array_merge( $rules, $this->getEmployeeValidationRules( $context ) );
		}

		return $rules;
	}

	/**
	 * Get employee-specific validation rules
	 *
	 * @param string $context Validation context.
	 *
	 * @return array
	 */
	private function getEmployeeValidationRules( $context ): array {
		$data = array(
			// 'services'                             => array( new ArrayRule() ),
			// 'services.*.service_id'                => array( new RequiredRule(), new NumericRule() ),
			// 'services.*.price'                     => array(
			// new RequiredRule(),
			// new NumericRule(),
			// new MinRule( 0 ),
			// ),
			// 'services.*.min_capacity'              => array(
			// new RequiredRule(),
			// new NumericRule(),
			// new MinRule( 1 ),
			// ),
			// 'services.*.max_capacity'              => array(
			// new RequiredRule(),
			// new NumericRule(),
			// new MinRule( 1 ),
			// ),
			// Custom pricing validation - Fixed structure
			// 'services.*.custom_pricing'            => array( new ArrayRule() ),
			// 'services.*.custom_pricing.enabled'    => array( new BooleanRule() ),
			// 'services.*.custom_pricing.durations'  => array( new ArrayRule() ),

			// 'days_off.*.name'                      => array(
			// new RequiredRule(),
			// new StringRule(),
			// new MaxRule( 100 ),
			// ),
			// 'days_off.*.start_date'                => array( new RequiredRule(), new DateRule() ),
			// 'days_off.*.end_date'                  => array( new RequiredRule(), new DateRule() ),
			// 'days_off.*.recurring'                 => array( new BooleanRule() ),
			//
			// 'weekdays'                             => array( new ArrayRule() ),
			// 'weekdays.*.day_index'                 => array( new RequiredRule(), new NumericRule() ),
			// 'weekdays.*.start_time'                => array( new RequiredRule(), new TimeRule() ),
			// 'weekdays.*.end_time'                  => array( new RequiredRule(), new TimeRule() ),
			//
			// 'weekdays.*.time_breaks'               => array( new ArrayRule() ),
			//
			// 'weekdays.*.time_breaks.*.start_time'  => array( new RequiredRule(), new TimeRule() ),
			// 'weekdays.*.time_breaks.*.end_time'    => array( new RequiredRule(), new TimeRule() ),
			// 'weekdays.*.time_periods'              => array( new ArrayRule() ),
			// 'weekdays.*.time_periods.*.start_time' => array( new RequiredRule(), new TimeRule() ),
			// 'weekdays.*.time_periods.*.end_time'   => array( new RequiredRule(), new TimeRule() ),
			//
			// 'weekdays.*.time_periods.*.locations'  => array( new ArrayRule() ),
			// 'weekdays.*.time_periods.*.locations.*.location_id' => array( new RequiredRule(), new NumericRule() ),
			//
			// 'weekdays.*.time_periods.*.services'   => array( new ArrayRule() ),
			// 'weekdays.*.time_periods.*.services.*.service_id' => array( new RequiredRule(), new NumericRule() ),
		);

		$updateData = array(
			// Services validation - Fixed to handle your JSON structure
			'services.*.id' => array( new NumericRule() ),

			// Days off validation - Fixed field name from 'recurring' to 'repeat'
			// 'days_off'                                 => array( new ArrayRule() ),
			// 'days_off.*.id'                            => array( new NumericRule() ),
			// 'days_off.*.user_id'                       => array( new NumericRule() ),
			//
			// Weekdays validation
			// 'weekdays.*.id'                            => array( new NumericRule() ),
			// 'weekdays.*.user_id'                       => array( new NumericRule() ),
			//
			// Time breaks validation
			// 'weekdays.*.time_breaks.*.id'              => array( new NumericRule() ),
			// 'weekdays.*.time_breaks.*.weekday_id'      => array( new NumericRule() ),
			//
			// Time periods validation
			// 'weekdays.*.time_periods.*.id'             => array( new NumericRule() ),
			// 'weekdays.*.time_periods.*.weekday_id'     => array( new NumericRule() ),
			//
			// Locations validation
			// 'weekdays.*.time_periods.*.locations.*.id' => array( new NumericRule() ),
			// 'weekdays.*.time_periods.*.locations.*.weekday_time_period_id' => array( new NumericRule() ),
			//
			// Services in time periods validation
			// 'weekdays.*.time_periods.*.services.*.id'  => array( new NumericRule() ),
			// 'weekdays.*.time_periods.*.services.*.weekday_time_period_id' => array( new NumericRule() ),
		);

		if ( $context === 'update' ) {
			$data = array_merge( $data, $updateData );
		}

		return $data;
	}

	/**
	 * Apply conditional validation rules based on data.
	 *
	 * @param array $rules Base validation rules.
	 * @param array $data Request data.
	 * @param string $context Validation context.
	 *
	 * @return array
	 */
	protected function applyConditionalRules( array $rules, array $data, string $context ): array {
		// Remove service_id requirement if it's not provided for any service
		return $rules;
	}

	/**
	 * Transform an item into a structured array
	 *
	 * @param mixed $item The item to be transformed
	 *
	 * @return array The transformed item as an array
	 */
	protected function transformItem( $item ): array {
		return ( new UserResource() )->transform( $item );
	}

	/**
	 * Transforms a collection of items by sorting them in the specified direction
	 * and converting them into resources.
	 *
	 * @param array $items The collection of items to be transformed.
	 * @param string $direction The sorting direction, either 'ASC' or 'DESC' (default: 'DESC').
	 *
	 * @return array The transformed collection of resources.
	 */
	protected function transformCollection( array $items, string $direction = 'DESC' ): array {
		// Sort original items first
//		usort(
//			$items,
//			function ( $a, $b ) use ( $direction ) {
//				// Use the actual sortable field, e.g., tsta or id
//				$aVal = $a->id;
//				$bVal = $b->id;
//
//				// If it's a date, convert to timestamp
//				if ( strtotime( $aVal ) && strtotime( $bVal ) ) {
//					$aVal = strtotime( $aVal );
//					$bVal = strtotime( $bVal );
//				}
//
//				return 'DESC' === $direction
//					? $bVal <=> $aVal
//					: $aVal <=> $bVal;
//			}
//		);

		// Then transform into resources
		return UserResource::collection( $items );
	}


	/**
	 * Extract profile data based on fillable fields
	 *
	 * @param array $data The input data array containing potential profile information
	 *
	 * @return array The filtered array containing only the fields defined as fillable in the UserProfile model
	 */
	private function extractProfileData( array $data ): array {
		$fillable = ( new UserProfile() )->getFillable();

		return array_intersect_key( $data, array_flip( $fillable ) );
	}

	/**
	 * Handles the event when a user is created.
	 *
	 * @param mixed $data The data associated with the user creation event.
	 * @param mixed $item The item representing the user being created.
	 * @param mixed $request The request object containing additional information.
	 * @param mixed $controller The controller handling this operation.
	 *
	 * @return void
	 */
	public function handleUserCreated( $data, $item, $request, $controller ): void {
		$this->createOrUpdateProfile( $item, $request, true );
	}

	/**
	 * Handle user updated event by creating or updating the user profile.
	 *
	 * @param mixed $item The user item containing relevant data.
	 * @param mixed $request The request object holding additional user update details.
	 * @param mixed $controller The controller instance managing the operation.
	 *
	 * @return void
	 */
	public function handleUserUpdated( $item, $request, $controller ): void {
		$this->createOrUpdateProfile( $item, $request, false );
	}

	/**
	 * Create or update a user profile based on the provided data.
	 *
	 * @param object $item The user item object containing data, including the user ID.
	 * @param WP_REST_Request $request The current REST request, containing JSON parameters.
	 * @param bool $isCreate Indicates whether to create a new profile (true) or update an existing one (false). Default is true.
	 *
	 * @return void
	 * @throws \Exception If an error occurs during the profile creation or update process.
	 */
	private function createOrUpdateProfile( $item, WP_REST_Request $request, bool $isCreate = true ): void {
		if ( ! $item->id ) {
			return;
		}

		$service                = Container::resolve( UserService::class );
		$profileData            = $this->extractProfileData( $request->get_json_params() );
		$profileData['user_id'] = $item->id;

		$item->load( 'profile' );
		if ( $isCreate ) {
			$service->createUserWithProfile( $profileData );
		} else {
			$service->updateUserProfile( $item->id, $profileData );
		}

		if ( ! empty( $profileData['wp_user_id'] ) && 'create_new' === $profileData['wp_user_id'] ) {
			$service->createWpUser( $item->id, $profileData );
		}

		if ( $item->get_type() === 'employee' ) {
			$this->syncEmployeeData( $service, $item->id, $request );
		}

		$item->load( 'services' );
	}

	/**
	 * Synchronize employee data by processing parameters from the request and invoking the appropriate service methods.
	 *
	 * @param UserService $service The service responsible for handling user-related operations.
	 * @param int $user_id The ID of the user whose data is being synchronized.
	 * @param WP_REST_Request $request The request instance containing employee data for synchronization.
	 *
	 * @return void
	 */
	private function syncEmployeeData( UserService $service, int $user_id, WP_REST_Request $request ): void {
		$syncMap = array(
			'services'     => 'createOrUpdateUserServices',
			'days_off'     => 'createOrUpdateUserDaysOff',
			'weekdays'     => 'createOrUpdateUserWeekdays',
			'special_days' => 'createOrUpdateUserSpecialDays',
		);

		foreach ( $syncMap as $key => $method ) {
			$data = $request->get_param( $key );
			if ( is_array( $data ) ) {
				$service->$method( $user_id, $data );
			}
		}
	}

	/**
	 * Display the item with its associated profile.
	 *
	 * @param mixed $item The item to be loaded with its profile.
	 * @param mixed $id The identifier for the item.
	 * @param mixed $controller The controller handling the request.
	 *
	 * @return mixed The item with the loaded profile.
	 */
	public function showWithProfile( $item, $id, $controller ) {
		$item->load( 'profile' );

		return $item;
	}

	/**
	 * Show items with their profile index
	 *
	 * @param array $items An array of items to be processed.
	 * @param mixed $request The request data for additional processing.
	 *
	 * @return array The processed array of items with the 'profile' relationships loaded.
	 */
//	public function showWithProfileIndex( $items, $request ): array {
//		if ( empty( $items ) || ! is_array( $items ) ) {
//			return $items;
//		}
//		$itemIds = array_map( fn( $item ) => $item->id, $items );
//		$items   = $this->repository->findMany( $itemIds );
//
//		return $this->repository->loadRelationships( $items, array( 'profile' ) );
//	}

	public function showWithProfileIndex( $items, $request ): array {
		if ( empty( $items ) || ! is_array( $items ) ) {
			return $items;
		}
		$sortedIds = array_map( fn( $item ) => $item->id, $items );

		$fetched = $this->repository->findMany( $sortedIds );
		$this->repository->loadRelationships( $fetched, array( 'profile' ) );

		// Re-index by ID.
		$indexed = array();
		foreach ( $fetched as $item ) {
			$indexed[ $item->id ] = $item;
		}

		// Restore original sort order.
		$ordered = array();
		foreach ( $sortedIds as $id ) {
			if ( isset( $indexed[ $id ] ) ) {
				$ordered[] = $indexed[ $id ];
			}
		}

		return $ordered;
	}

	/**
	 * Show the user item after it is created or updated.
	 *
	 * @param mixed $item The user item instance.
	 * @param mixed $request The request object.
	 * @param mixed $controller The controller handling the request.
	 *
	 * @return mixed
	 */
	public function showAfterCreateOrUpdateUser( $item, $request, $controller ) {
		$item->load( 'profile' );

		return $item;
	}

	/**
	 * Retrieve the profile information of a user.
	 *
	 * @param WP_REST_Request $request The request object containing user ID as a parameter.
	 *
	 * @return WP_Error|WP_REST_Response A response containing the user profile data or an error if the user is not found.
	 */
	public function getProfile( WP_REST_Request $request ) {
		$user_id = $request->get_param( 'user_id' );
		$user    = User::find( $user_id );

		if ( ! $user ) {
			return new WP_Error( 'not_found', 'User not found', array( 'status' => 404 ) );
		}

		$user->load( 'profile', 'appointments' );

		return rest_ensure_response( ( new UserResource() )->transform( $user ) );
	}

	/**
	 * Handles the dashboard data retrieval for a customer.
	 *
	 * @param WP_REST_Request $request The REST request instance containing parameters and data.
	 *
	 * @return mixed
	 */
	public function getDashboard( WP_REST_Request $request ) {
		return $this->applyMiddleware(
			$request,
			function ( $request ) {

				$totalSpend           = $this->repository->getCustomerTotalPaymentAmount();
				$totalCustomers       = $this->repository->getTotalUsersByType();
				$totalActiveCustomers = $this->repository->getTotalUsersByTypeAndStatus();
				$item                 = array(
					'total_spend'            => $totalSpend,
					'total_customers'        => $totalCustomers,
					'total_active_customers' => $totalActiveCustomers,
					'currency'               => array(
						'currency_symbol' => get_rtrb_currency_symbol(),
						'currency'        => get_rtrb_currency(),
					),
				);

				if ( ! $item ) {
					return ApiResponse::notFound()->send();
				}

				/**
				 * Filter the single item before returning it.
				 */
				$item = apply_filters( 'radius_booking_dashboard_customer', $item, $request, $this );

				return ApiResponse::success( $item )->send();
			}
		);
	}

	/**
	 * Handle bulk deletion of resources based on provided IDs.
	 *
	 * @param WP_REST_Request $request The request object containing parameters, including 'ids'.
	 *
	 * @return mixed The response after processing the bulk delete operation.
	 */
	public function bulkDelete( WP_REST_Request $request ) {
		return $this->applyMiddleware(
			$request,
			function ( $request ) {
				$ids = $request->get_param( 'ids' );
				if ( empty( $ids ) || ! is_array( $ids ) || ! array_filter( $ids, 'is_numeric' ) ) {
					return ApiResponse::notFound( 'Invalid IDs' )->send();
				}
				$deletedCount = User::deleteMany( $ids );

				return ApiResponse::success( array( 'deleted_count' => $deletedCount ) )->send();
			}
		);
	}

	/**
	 * Update the status of a user.
	 *
	 * @param WP_REST_Request $request The request object containing user ID and status parameters.
	 *
	 * @return mixed The response after updating the user status.
	 */
	public function updateStatus( WP_REST_Request $request ) {
		return $this->applyMiddleware(
			$request,
			function ( $request ) {
				$user_id = $request->get_param( 'user_id' );
				$status  = $request->get_param( 'status' );
				if ( ! $user_id || ! is_numeric( $user_id ) || ! in_array(
					$status,
					array(
						'active',
						'inactive',
						'pending',
						'draft'
					),
					true
				) ) {
					return ApiResponse::notFound( 'Invalid user ID or status' )->send();
				}

				$user = User::find( $user_id );
				if ( ! $user ) {
					return ApiResponse::notFound( 'User not found' )->send();
				}

				$user->status = $status;
				$user->save();

				return ApiResponse::success( array( 'id' => $user_id ) )->send();
			}
		);
	}

	/**
	 * Set conditions based on a query string and return parsed condition data.
	 *
	 * @param string $where The query string to parse into conditions.
	 * @param mixed $request The current request object or data.
	 * @param mixed $controller The controller handling the request.
	 *
	 * @return array An array of parsed condition data including columns, operators, and values.
	 */
	public function setCondition( $where, $request, $controller ) {
		if ( empty( $where ) ) {
			return array();
		}

		$where     = urldecode( $where );
		$wheres    = explode( '&', $where );
		$whereData = array();
		foreach ( $wheres as $whereItem ) {
			foreach ( $this->allowedOperators as $operator ) {
				if ( strpos( $whereItem, $operator ) !== false ) {
					[ $column, $value ] = explode( $operator, $whereItem, 2 );
					if ( 'all' === $value ) {
						continue;
					}
					if ( ! empty( $column ) && isset( $value ) ) {
						$value = trim( $value );

						// ✅ If value contains comma → use IN operator
						if ( str_contains( $value, ',' ) ) {
							$value       = array_map( 'trim', explode( ',', $value ) );
							$whereData[] = array(
								'column'   => trim( $column ),
								'operator' => 'IN',
								'value'    => $value,
							);
						} else {
							$whereData[] = array(
								'column'   => trim( $column ),
								'operator' => $operator,
								'value'    => $value,
							);
						}
					}
					break;
				}
			}
		}

		return $whereData;
	}


	public function getEmployees( WP_REST_Request $request ) {
		return $this->applyMiddleware(
			$request,
			function ( $request ) {
				$get_employee = $this->repository->getEmployees();

				return ApiResponse::success( $get_employee, 'Resource fetch successfully' )->send();
			}
		);
	}
}
