<?php

namespace RadiusTheme\RadiusBooking\Services;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly
}
use Exception;
use RadiusTheme\RadiusBooking\Repositories\UserProfileRepository;
use RadiusTheme\RadiusBooking\Repositories\UserRepository;
use RadiusTheme\RadiusBooking\Services\EmployeeServices\ServiceSyncService;
use RadiusTheme\RadiusBooking\Services\EmployeeServices\DaysOffSyncService;
use RadiusTheme\RadiusBooking\Services\EmployeeServices\SpecialDaySyncService;
use RadiusTheme\RadiusBooking\Services\EmployeeServices\WeekdaySyncService;

/**
 * Class UserService
 *
 * Main service for user operations - delegates to specialized services
 *
 * @package RadiusTheme\RadiusBooking\Services
 * @since 1.0.0
 */
class UserService {

	/**
	 * User repository instance.
	 *
	 * @var UserRepository
	 */
	private UserRepository $userRepo;
	/**
	 * User profile repository instance.
	 *
	 * @var UserProfileRepository
	 */
	private UserProfileRepository $userProfileRepo;
	/**
	 * Service synchronization service instance.
	 *
	 * @var ServiceSyncService
	 */
	private ServiceSyncService $serviceSyncService;
	/**
	 * Days off synchronization service instance.
	 *
	 * @var DaysOffSyncService
	 */
	private DaysOffSyncService $daysOffSyncService;
	/**
	 * Weekday synchronization service instance.
	 *
	 * @var WeekdaySyncService
	 */
	private WeekdaySyncService $weekdaySyncService;

	/**
	 * Special day synchronization service instance.
	 *
	 * @var SpecialDaySyncService
	 */
	private SpecialDaySyncService $specialDaySyncService;

	/**
	 * Constructor method to initialize dependencies.
	 *
	 * @param UserRepository $userRepo User repository instance.
	 * @param UserProfileRepository $userProfileRepo User profile repository instance.
	 * @param ServiceSyncService $serviceSyncService Service synchronization service instance.
	 * @param DaysOffSyncService $daysOffSyncService Days off synchronization service instance.
	 * @param WeekdaySyncService $weekdaySyncService Weekday synchronization service instance.
	 * @param SpecialDaySyncService $specialDaySyncService Special day synchronization service instance.
	 */
	public function __construct(
		UserRepository $userRepo,
		UserProfileRepository $userProfileRepo,
		ServiceSyncService $serviceSyncService,
		DaysOffSyncService $daysOffSyncService,
		WeekdaySyncService $weekdaySyncService,
		SpecialDaySyncService $specialDaySyncService
	) {
		$this->userRepo              = $userRepo;
		$this->userProfileRepo       = $userProfileRepo;
		$this->serviceSyncService    = $serviceSyncService;
		$this->daysOffSyncService    = $daysOffSyncService;
		$this->weekdaySyncService    = $weekdaySyncService;
		$this->specialDaySyncService = $specialDaySyncService;
	}

	/**
	 * Creates a new user and their associated profile.
	 *
	 * @param array $data The data required for user creation.
	 *
	 * @return int|array Returns the ID of the created user on success, or an error response array on failure.
	 * @throws Exception If an error occurs during user creation.
	 */
	public function createUser( $data ) {
		try {
			$user    = $this->userRepo->createUser( $data );
			$user_id = $user->get_id();
			if ( ! $user_id ) {
				throw new Exception( 'User creation failed' );
			}
			// Create user profile
			$data['user_id'] = $user_id;
			$this->createUserWithProfile( $data );

			return $user->get_id();
		} catch ( Exception $e ) {
			return $this->errorResponse( $e->getMessage() );
		}
	}

	/**
	 * Creates a user profile and returns a success response.
	 *
	 * @param array $data The data required to create the user profile.
	 *
	 * @return array The response indicating the result of the operation.
	 */
	public function createUserWithProfile( array $data ): array {
		try {
			$profile = $this->userProfileRepo->createProfile( $data );

			return $this->successResponse( $profile, 'profile' );
		} catch ( Exception $e ) {
			return $this->errorResponse( $e->getMessage() );
		}
	}

	/**
	 * Creates a WordPress user and associates them with a profile.
	 *
	 * @param int|string $id The ID of the user to create a WordPress account for.
	 * @param array $data An associative array of user data, including optional 'first_name' and 'last_name' fields.
	 *
	 * @return array Returns a success response with the profile data or an error response if an exception occurs.
	 * @throws Exception If the user is not found or an error occurs during the WordPress user creation process.
	 */
	public function createWpUser( $id, $data ): array {
		try {
			$this->userRepo->existUser( $id );
			$user = $this->userRepo->findOrFail( $id );
			if ( ! $user ) {
				throw new Exception( 'User not found' );
			}
			$email              = $user->get_email();
			$userData           = array(
				'username'   => $this->userProfileRepo->getWPUserName( $email ),
				'email'      => $user->get_email(),
				'first_name' => $data['first_name'] ?? '',
				'last_name'  => $data['last_name'] ?? '',
				'password'   => wp_generate_password(),
			);
			$userData['role']   = $this->userProfileRepo->setUserRole( $user );
			$wp_user_id         = $this->userProfileRepo->createWpUser( $userData );
			$data['wp_user_id'] = $wp_user_id;
			$profile            = $this->userProfileRepo->createOrUpdateWpUser( $id, $data );

			return $this->successResponse( $profile, 'profile' );
		} catch ( Exception $e ) {
			return $this->errorResponse( $e->getMessage() );
		}
	}

	/**
	 * Updates the user's profile with the provided data.
	 *
	 * @param int $userId The unique identifier of the user.
	 * @param array $data An associative array containing the profile data to be updated.
	 *
	 * @return array The response containing the status of the update operation.
	 */
	public function updateUserProfile( int $userId, array $data ): array {
		try {
			$this->userRepo->existUser( $userId );
			$result = $this->userProfileRepo->updateProfile( $userId, $data );

			return $this->successResponse( $result, 'result' );
		} catch ( Exception $e ) {
			return $this->errorResponse( $e->getMessage() );
		}
	}

	/**
	 * Create or update user services by synchronizing the provided services for a specified user.
	 *
	 * @param int $userId The ID of the user whose services need to be synchronized.
	 * @param array $userServices An array of services to be synchronized for the user.
	 *
	 * @return array The response indicating the status of the operation.
	 */
	public function createOrUpdateUserServices( int $userId, array $userServices ): array {
		try {
			$this->userRepo->existUser( $userId );
			$this->serviceSyncService->syncServices( $userId, $userServices );

			return $this->successResponse( array(), 'message', 'Services synchronized successfully' );
		} catch ( Exception $e ) {
			return $this->errorResponse( $e->getMessage() );
		}
	}

	/**
	 * Creates or updates the days off for a user.
	 *
	 * @param int $userId The ID of the user whose days off are to be created or updated.
	 * @param array $daysOff List of days off to be synchronized for the user.
	 *
	 * @return array The response indicating the success or failure of the operation.
	 */
	public function createOrUpdateUserDaysOff( int $userId, array $daysOff ): array {
		try {
			$this->userRepo->existUser( $userId );
			$this->daysOffSyncService->syncDaysOff( $userId, $daysOff );

			return $this->successResponse( array(), 'message', 'Days off synchronized successfully' );
		} catch ( Exception $e ) {
			return $this->errorResponse( $e->getMessage() );
		}
	}

	/**
	 * Create or update the weekdays associated with a user.
	 *
	 * @param int $userId The ID of the user whose weekdays are being updated.
	 * @param array $weekdays An array representing the weekdays to be synchronized with the user.
	 *
	 * @return array The response indicating the success or failure of the operation.
	 */
	public function createOrUpdateUserWeekdays( int $userId, array $weekdays ): array {
		try {
			$this->userRepo->existUser( $userId );
			$this->weekdaySyncService->syncWeekdays( $userId, $weekdays );

			return $this->successResponse( array(), 'message', 'Weekdays synchronized successfully' );
		} catch ( Exception $e ) {
			return $this->errorResponse( $e->getMessage() );
		}
	}

	/**
	 * Create or update special days for a user.
	 *
	 * @param int $userId The ID of the user.
	 * @param array $specificDays An array of specific days to be synchronized for the user.
	 *
	 * @return array The response indicating the success or failure of the operation.
	 */
	public function createOrUpdateUserSpecialDays( int $userId, array $specificDays ): array {
		try {
			$this->userRepo->existUser( $userId );
			$this->specialDaySyncService->syncSpecialDays( $userId, $specificDays );

			return $this->successResponse( array(), 'message', 'Specific days synchronized successfully' );
		} catch ( Exception $e ) {
			return $this->errorResponse( $e->getMessage() );
		}
	}

	/**
	 * Retrieves the list of services associated with a given user.
	 *
	 * @param int $userId The ID of the user whose services are to be retrieved.
	 *
	 * @return array An array containing the services associated with the user; returns an empty array on failure.
	 */
	public function getUserServices( int $userId ): array {
		try {
			$this->userRepo->existUser( $userId );

			return $this->userRepo->getUserServices( $userId );
		} catch ( Exception $e ) {
			return array();
		}
	}

	/**
	 * Retrieves the days off for a user based on their ID.
	 *
	 * @param int $userId The unique identifier of the user.
	 *
	 * @return array An array containing the user's days off. Returns an empty array if an error occurs.
	 */
	public function getUserDaysOff( int $userId ): array {
		try {
			$this->userRepo->existUser( $userId );

			return $this->userRepo->getUserDaysOff( $userId );
		} catch ( Exception $e ) {
			return array();
		}
	}

	/**
	 * Retrieves all employees with their profile details.
	 *
	 * @return array List of employee records.
	 */
	public function getAllEmployees(): array {
		$employees = $this->userRepo->getEmployees();
		$result    = array();
		if ( ! empty( $employees ) ) {
			foreach ( $employees as $employee ) {
				$profile  = $this->userProfileRepo->findBy( array( 'user_id' => (int) $employee->id ) );
				$result[] = array(
					'id'         => (int) $employee->id,
					'email'      => $employee->email,
					'first_name' => $profile ? $profile->first_name : '',
					'last_name'  => $profile ? $profile->last_name : '',
					'phone'      => $profile ? $profile->phone : '',
					'status'     => $employee->status ?? 'active',
				);
			}
		}
		return $result;
	}

	/**
	 * Creates a new employee (user + profile + optional WP user).
	 *
	 * @param array $data Employee data including first_name, last_name, email, phone.
	 *
	 * @return array|\WP_Error Created employee data or error.
	 */
	public function createEmployee( array $data ) {
		try {
			// Check for duplicate email.
			$existing = $this->userRepo->findByEmail( $data['email'] ?? '' );
			if ( $existing ) {
				return new \WP_Error( 'duplicate_email', __( 'An employee with this email already exists.', 'radius-booking' ) );
			}

			// Create the user record.
			$user_data = array(
				'email'     => sanitize_email( $data['email'] ),
				'user_type' => 'employee',
				'status'    => sanitize_text_field( $data['status'] ?? 'active' ),
				'password'  => wp_generate_password(),
			);

			if ( ! empty( $data['created_by'] ) ) {
				$user_data['created_by'] = (int) $data['created_by'];
			}

			$user    = $this->userRepo->createUser( $user_data );
			$user_id = $user->get_id();

			if ( ! $user_id ) {
				return new \WP_Error( 'creation_failed', __( 'Failed to create employee record.', 'radius-booking' ) );
			}

			// Create profile.
			$profile_data = array(
				'user_id'    => $user_id,
				'first_name' => sanitize_text_field( $data['first_name'] ?? '' ),
				'last_name'  => sanitize_text_field( $data['last_name'] ?? '' ),
				'phone'      => sanitize_text_field( $data['phone'] ?? '' ),
			);
			if ( isset( $data['note'] ) ) {
				$profile_data['note'] = sanitize_textarea_field( $data['note'] );
			}
			if ( isset( $data['time_zone'] ) ) {
				$profile_data['time_zone'] = sanitize_text_field( $data['time_zone'] );
			}
			if ( isset( $data['image_id'] ) ) {
				$profile_data['image_id'] = absint( $data['image_id'] );
			}
			$this->userProfileRepo->createProfile( $profile_data );

			// Create WP user.
			$this->createWpUser( $user_id, $profile_data );

			return array(
				'id'         => $user_id,
				'email'      => $user_data['email'],
				'first_name' => $profile_data['first_name'],
				'last_name'  => $profile_data['last_name'],
				'phone'      => $profile_data['phone'],
				'status'     => $user_data['status'],
				'note'       => $profile_data['note'] ?? '',
				'time_zone'  => $profile_data['time_zone'] ?? '',
				'image_id'   => $profile_data['image_id'] ?? 0,
			);
		} catch ( Exception $e ) {
			return new \WP_Error( 'creation_failed', $e->getMessage() );
		}
	}

	/**
	 * Updates an existing employee's profile data.
	 *
	 * @param int   $staff_id The employee user ID.
	 * @param array $data     Updated data (first_name, last_name, phone, status).
	 *
	 * @return array|\WP_Error Updated employee data or error.
	 */
	public function updateEmployee( int $staff_id, array $data ) {
		try {
			$this->userRepo->existUser( $staff_id );

			// Update profile fields.
			$profile_data = array();
			if ( isset( $data['first_name'] ) ) {
				$profile_data['first_name'] = sanitize_text_field( $data['first_name'] );
			}
			if ( isset( $data['last_name'] ) ) {
				$profile_data['last_name'] = sanitize_text_field( $data['last_name'] );
			}
			if ( isset( $data['phone'] ) ) {
				$profile_data['phone'] = sanitize_text_field( $data['phone'] );
			}
			if ( isset( $data['note'] ) ) {
				$profile_data['note'] = sanitize_textarea_field( $data['note'] );
			}
			if ( isset( $data['time_zone'] ) ) {
				$profile_data['time_zone'] = sanitize_text_field( $data['time_zone'] );
			}
			if ( isset( $data['image_id'] ) ) {
				$profile_data['image_id'] = absint( $data['image_id'] );
			}

			if ( ! empty( $profile_data ) ) {
				$this->userProfileRepo->updateProfile( $staff_id, $profile_data );
			}

			// Update user status if provided.
			if ( isset( $data['status'] ) ) {
				$user = $this->userRepo->findOrFail( $staff_id );
				$user->fill( array( 'status' => sanitize_text_field( $data['status'] ) ) );
				$user->save();
			}

			$profile = $this->userProfileRepo->findBy( array( 'user_id' => $staff_id ) );

			return array(
				'id'         => $staff_id,
				'email'      => $this->userRepo->findOrFail( $staff_id )->email,
				'first_name' => $profile ? $profile->first_name : '',
				'last_name'  => $profile ? $profile->last_name : '',
				'phone'      => $profile ? $profile->phone : '',
				'status'     => $data['status'] ?? 'active',
				'note'       => $profile ? ( $profile->note ?? '' ) : '',
				'time_zone'  => $profile ? ( $profile->time_zone ?? '' ) : '',
				'image_id'   => $profile ? ( $profile->image_id ?? 0 ) : 0,
			);
		} catch ( Exception $e ) {
			return new \WP_Error( 'update_failed', $e->getMessage() );
		}
	}

	public function updatePassword( $user_id, $current_password, $new_password ) {
		try {

			$userProfile = $this->userProfileRepo->findProfileById( $user_id );

			if ( ! $userProfile ) {
				return $this->errorResponse( __( 'User profile not found.', 'radius-booking' ) );
			}

			$wp_user_id = $userProfile->wp_user_id;
			$wp_user    = get_user_by( 'ID', $wp_user_id );

			if ( ! $wp_user ) {
				return $this->errorResponse( __( 'User not found.', 'radius-booking' ) );
			}

			// Validate current password
			if ( ! wp_check_password( $current_password, $wp_user->user_pass, $wp_user_id ) ) {
				return array(
					'success' => false,
					'message' => __( 'Current password is incorrect.', 'radius-booking' ),
				);
			}

			// Update password
			wp_set_password( $new_password, $wp_user_id );
			// 🔥 IMPORTANT: Re-login the user
			wp_set_current_user( $wp_user_id );
			wp_set_auth_cookie( $wp_user_id );
			return array(
				'success' => true,
				'message' => __( 'Password changed successfully.', 'radius-booking' ),
			);

		} catch ( Exception $e ) {
			return $this->errorResponse( $e->getMessage() );
		}
	}



	/**
	 * Builds and returns a success response array with a customizable key and optional message.
	 *
	 * @param mixed $data The data to include as the value of the key in the response.
	 * @param string $key The key under which the data will be stored in the response.
	 * @param string|null $message An optional message to include in the response.
	 *
	 * @return array The constructed response array containing a success status, the key-value pair, and an optional message.
	 */
	private function successResponse( $data, string $key, ?string $message = null ): array {
		$response = array(
			'success' => true,
			$key      => $data,
		);
		if ( $message ) {
			$response['message'] = $message;
		}

		return $response;
	}

	/**
	 * Builds and returns an error response array with a specified error message.
	 *
	 * @param string $message The error message to include in the response.
	 *
	 * @return array The constructed response array containing a failure status and the error message.
	 */
	private function errorResponse( string $message ): array {
		return array(
			'success' => false,
			'error'   => $message,
		);
	}
}
