<?php
/**
 * RadiusBooking API Route Registrar
 *
 * This class is responsible for registering API routes dynamically with the WordPress REST API.
 * It supports CRUD operations and allows for middleware to be applied globally or per route.
 *
 * @package RadiusTheme\RadiusBooking\Core\Api\Routes
 */

namespace RadiusTheme\RadiusBooking\Repositories;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
} // Exit if accessed directly
use RadiusTheme\RadiusBooking\Abstracts\BaseRepository;
use RadiusTheme\RadiusBooking\Core\ORM\PaginationResult;
use RadiusTheme\RadiusBooking\Core\Support\Collection;
use RadiusTheme\RadiusBooking\Models\Category;
use RadiusTheme\RadiusBooking\Models\CustomerBooking;
use RadiusTheme\RadiusBooking\Models\EmployeeDayoff;
use RadiusTheme\RadiusBooking\Models\EmployeeService;
use RadiusTheme\RadiusBooking\Models\EmployeeSpecialDay;
use RadiusTheme\RadiusBooking\Models\EmployeeSpecialDayTimePeriod;
use RadiusTheme\RadiusBooking\Models\EmployeeSpecialDayTimePeriodLocation;
use RadiusTheme\RadiusBooking\Models\EmployeeSpecialDayTimePeriodService;
use RadiusTheme\RadiusBooking\Models\EmployeeWeekday;
use RadiusTheme\RadiusBooking\Models\EmployeeWeekdayBreakTime;
use RadiusTheme\RadiusBooking\Models\EmployeeWeekdayTimePeriod;
use RadiusTheme\RadiusBooking\Models\EmployeeWeekdayTimePeriodLocation;
use RadiusTheme\RadiusBooking\Models\EmployeeWeekdayTimePeriodService;
use RadiusTheme\RadiusBooking\Models\Service;
use RadiusTheme\RadiusBooking\Models\User;
use RadiusTheme\RadiusBooking\Models\UserProfile;
use RadiusTheme\RadiusBooking\Services\EmployeeServices\UserWeekdayDataService;

/**
 * UserRepository
 *
 * Handles data operations related to the User model.
 *
 * @package RadiusTheme\RadiusBooking\Repositories
 */
class UserRepository extends BaseRepository {

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

	/**
	 * Find a user by their email address.
	 *
	 * @param string $email The email address to search for.
	 *
	 * @return User|null The User model if found, or null if not found.
	 */
	public function findByEmail( string $email ): ?User {
		$data = User::where( 'email', $email )->first();

		return $data ? $this->find( $data->id ) : null;
	}

	/**
	 * Get the total count of users by type.
	 *
	 * This method retrieves the number of users from the database filtered by their type.
	 *
	 * @param string $type The type of users to count. Defaults to 'customer'.
	 *
	 * @return int The total count of users of the specified type.
	 */
	public function getTotalUsersByType( $type = 'customer' ): int {
		// Use the model's query builder to count users by type
		return User::where( 'user_type', $type )->count();
	}

	/**
	 * Get the total number of users by their status.
	 *
	 * This method counts and returns the number of users with the specified status.
	 *
	 * @param string $status The status of the users to count. Defaults to 'active'.
	 *
	 * @return int The total number of users matching the given status.
	 */
	public function getTotalUsersByStatus( $status = 'active' ): int {
		// Use the model's query builder to count users by status
		return User::where( 'status', $status )->count();
	}

	/**
	 * Retrieve the total number of users based on their type and status.
	 *
	 * This method uses the query builder to count the number of users filtered by the specified type and status.
	 *
	 * @param string $type The user type to filter by. Defaults to 'customer'.
	 * @param string $status The user status to filter by. Defaults to 'active'.
	 *
	 * @return int The count of users matching the specified type and status.
	 */
	public function getTotalUsersByTypeAndStatus( $type = 'customer', $status = 'active' ): int {
		// Use the model's query builder to count users by type and status
		return User::where( 'user_type', $type )->where( 'status', $status )->count();
	}

	/**
	 * Retrieve the total order amount for all customers.
	 *
	 * This method calculates the sum of all order prices associated with customers
	 * by joining the users table with the customer bookings table. It specifically
	 * targets users with the type 'customer' and returns the total amount of their orders.
	 *
	 * @return float The total order amount for customers, or 0.00 if no data is found.
	 */
	public function getCustomersTotalOrderAmount() {
		$userData = User::query()->leftJoin( 'radius_booking_customer_bookings', 'radius_booking_users.id', '=', 'radius_booking_customer_bookings.customer_id' )
						->selectRaw( 'SUM(' . radius_table_prefix() . 'customer_bookings.price) as total' )
						->where( radius_table_prefix() . 'users.user_type', 'customer' )
						->first();

		return $userData->total ?? 0.00;
	}

	/**
	 * Retrieve the total payment amount for a customer.
	 *
	 * This method calculates the total sum of payment amounts made by customers, excluding
	 * payments with statuses 'refunded' or 'pending'. It performs a query with a join operation
	 * between users and payments tables and computes the total amount.
	 *
	 * @return float The total payment amount for the customer. Defaults to 0.00 if no payments are found.
	 */
	public function getCustomerTotalPaymentAmount() {
		$userData = User::query()->leftJoin( 'radius_booking_payments', 'radius_booking_users.id', '=', 'radius_booking_payments.customer_id' )
						->selectRaw( 'SUM(' . radius_table_prefix() . 'payments.amount) as total' )
						->where( radius_table_prefix() . 'users.user_type', 'customer' )
						->whereNotIn( radius_table_prefix() . 'payments.status', array( 'refunded', 'pending' ) )
						->first();

		return $userData->total ?? 0.00;
	}

	/**
	 * Retrieve the image URL for a given user profile.
	 *
	 * This method fetches the image URL of the user profile identified by the provided profile ID.
	 * If no profile is found, an empty string is returned.
	 *
	 * @param int $profileId The ID of the user profile to retrieve the image URL for.
	 *
	 * @return string The image URL of the user profile, or an empty string if the profile is not found.
	 */
	public function getImageUrl( $profileId ) {
		$profile = UserProfile::find( $profileId );
		if ( ! $profile ) {
			return '';
		}

		return $profile->getImageUrl();
	}

	/**
	 * Retrieve the user profile associated with a given user ID.
	 *
	 * This method fetches the first user profile that matches the specified user ID.
	 *
	 * @param int $user_id The ID of the user whose profile is to be retrieved.
	 *
	 * @return UserProfile|null The user profile if found, or null if no profile exists for the given user ID.
	 */
	public function get_profile( $user_id ) {
		$profile = UserProfile::where( 'user_id', $user_id )->first();
		if ( ! $profile ) {
			return null;
		}

		return $profile;
	}


	/**
	 * Paginate users with optional filtering, searching, and ordering.
	 *
	 * @param int $perPage Number of items per page.
	 * @param int $page Current page number.
	 * @param string|null $order Order direction ('asc' or 'desc').
	 * @param string $orderBy Column to order by.
	 * @param string|null $searchColumn Column(s) to search in.
	 * @param string|null $searchData Search term.
	 * @param array $where Additional where conditions.
	 *
	 * @return PaginationResult Paginated result set.
	 */
	public function paginate( int $perPage = 10, int $page = 1, $order = 'DESC', $orderBy = 'users.created_at', $searchColumn = null, $searchData = null, $where = array() ): PaginationResult {
		global $wpdb;
		$offset = ( $page - 1 ) * $perPage;

		// Start custom query
		$query = $this->model::query();

		// Join user_profiles table
		$query->leftJoin( 'radius_booking_user_profiles', 'radius_booking_users.id', '=', 'radius_booking_user_profiles.user_id' );

		// Always select user fields (to avoid conflicts)
		$query->select( $wpdb->prefix . 'radius_booking_users.*' );

		// Apply where conditions
		foreach ( $where as $condition ) {
			if ( ! isset( $condition['column'], $condition['operator'], $condition['value'] ) ) {
				continue;
			}

			if ( $condition['operator'] === 'IN' && is_array( $condition['value'] ) ) {
				$query->whereIn( $condition['column'], $condition['value'] );
			} else {
				$query->where( $condition['column'], $condition['operator'], $condition['value'] );
			}
		}

		// If search is needed, build separate queries for each search column and union them
		if ( $searchColumn && $searchData ) {
			$columns = is_array( $searchColumn ) ? $searchColumn : explode( ',', $searchColumn );
			$userIds = array();

			foreach ( $columns as $col ) {
				$searchQuery = $this->model::query();
				$searchQuery->leftJoin( 'radius_booking_user_profiles', 'radius_booking_users.id', '=', 'radius_booking_user_profiles.user_id' );
				$searchQuery->select( $wpdb->prefix . 'radius_booking_users.id' );

				// Apply same where conditions
				foreach ( $where as $condition ) {
					if ( ! isset( $condition['column'], $condition['operator'], $condition['value'] ) ) {
						continue;
					}
					if ( $condition['operator'] === 'IN' && is_array( $condition['value'] ) ) {
						$searchQuery->whereIn( $condition['column'], $condition['value'] );
					} else {
						$searchQuery->where( $condition['column'], $condition['operator'], $condition['value'] );
					}
				}

				// Apply search condition for this column
				$searchQuery->where( trim( $col ), 'LIKE', '%' . $searchData . '%' );

				// Get IDs that match this column search
				$columnResults = $searchQuery->get();
				foreach ( $columnResults as $result ) {
					$userIds[] = $result->id;
				}
			}

			// Remove duplicates and filter main query by found IDs
			$userIds = array_unique( $userIds );
			if ( ! empty( $userIds ) ) {
				$query->whereIn( $wpdb->prefix . 'radius_booking_users.id', $userIds );
			} else {
				// No results found, return empty result
				return new PaginationResult( array(), 0, $perPage, $page );
			}
		}

		// Count total records after all filtering
		$total = $query->count();

		// Apply ordering, limit, offset
		if ( $order ) {
			$query->orderBy( $orderBy, $order );
		}

		$items = $query->limit( $perPage )->offset( $offset )->get();

		return new PaginationResult( $items, $total, $perPage, $page );
	}

	/**
	 * Create a new user record.
	 *
	 * This method creates a new user in the database using the provided data.
	 *
	 * @param array $data An associative array containing the user attributes to create.
	 *
	 * @return User The created User instance.
	 */
	public function createUser( $data ) {
		return User::create( $data );
	}

	/**
	 * Find a user by their ID.
	 *
	 * @param int $id The ID of the user to find.
	 *
	 * @return User|null The User model if found, or null if not found.
	 */
	public function findById( int $id ): ?User {
		return User::find( $id );
	}

	/**
	 * Check if a user exists by their ID.
	 *
	 * This method verifies whether a user with the specified ID exists. If the user is not found,
	 * an exception is thrown.
	 *
	 * @param int $userId The ID of the user to check for existence.
	 *
	 * @return void
	 *
	 * @throws \InvalidArgumentException If the user does not exist.
	 */
	public function existUser( int $userId ): void {
		$user = $this->exists( $userId );
		if ( ! $user ) {
			throw new \InvalidArgumentException( 'User not found' );
		}
	}

	/**
	 * Find an existing EmployeeService by ID or create a new instance.
	 *
	 * This method attempts to locate an EmployeeService record using the provided ID in the service data.
	 * If the ID is not set or the EmployeeService is not found, a new instance of EmployeeService is created.
	 *
	 * @param array $serviceData An associative array of service data, potentially containing an 'id' key.
	 *
	 * @return EmployeeService The existing EmployeeService object if found, or a new EmployeeService instance if not.
	 *
	 * @throws \RuntimeException If the EmployeeService with the specified ID is not found.
	 */
	public function findOrCreateEmployeeService( array $serviceData ): EmployeeService {
		if ( isset( $serviceData['id'] ) ) {
			$employeeService = EmployeeService::find( $serviceData['id'] );
			if ( ! $employeeService ) {
				throw new \RuntimeException( "Employee service with ID {$serviceData['id']} not found" ); //phpcs:ignore
			}

			return $employeeService;
		}

		return new EmployeeService();
	}

	/**
	 * Retrieve an EmployeeService record by user ID and service ID.
	 *
	 * This method fetches the first record that matches the provided user and service IDs.
	 *
	 * @param int $userId The ID of the user associated with the service.
	 * @param int $serviceId The ID of the service to be retrieved.
	 *
	 * @return EmployeeService|null The matching EmployeeService object, or null if not found.
	 */
	public function findByUserAndService( int $userId, int $serviceId ): ?EmployeeService {
		return EmployeeService::where( 'user_id', $userId )->where( 'service_id', $serviceId )->first();
	}

	/**
	 * Update an existing EmployeeService with the provided data.
	 *
	 * This method updates the EmployeeService instance with new data such as
	 * service ID, price, capacity, and custom pricing, and persists the changes
	 * to the database. If saving the changes fails, an exception is thrown.
	 *
	 * @param EmployeeService $employeeService The EmployeeService instance to update.
	 * @param int $userId The ID of the user associated with the EmployeeService.
	 * @param array $serviceData An associative array containing service information,
	 *                           including 'service_id', 'price', 'min_capacity',
	 *                           'max_capacity', and optionally 'custom_pricing'.
	 *
	 * @return void This method does not return a value.
	 *
	 * @throws \RuntimeException If the EmployeeService fails to save.
	 */
	public function updateEmployeeService( EmployeeService $employeeService, int $userId, array $serviceData ): void {
		$employeeService->fill(
			array(
				'user_id'        => $userId,
				'service_id'     => $serviceData['service_id'],
				'price'          => $serviceData['price'] ?? 0,
				'min_capacity'   => $serviceData['min_capacity'] ?? 0,
				'max_capacity'   => $serviceData['max_capacity'] ?? 0,
				'custom_pricing' => $serviceData['custom_pricing'] ?? null,
			)
		);

		if ( ! $employeeService->save() ) {
			throw new \RuntimeException( 'Failed to save employee service' );
		}
	}

	/**
	 * Get all services associated with a user.
	 *
	 * @param int $userId The ID of the user
	 *
	 * @return array An array of services associated with the user
	 */
	public function getUserServices( int $userId ): array {
		$user = $this->findById( $userId );
		if ( ! $user ) {
			return array();
		}

		$services = $user->services()->get();
		if ( empty( $services ) ) {
			return array();
		}
		return $this->formatUserServices( $services );
	}

	/**
	 * Format user services into a structured array.
	 *
	 * @param array $services An array of EmployeeService models.
	 *
	 * @return array An array of formatted service data.
	 */
	public function formatUserServices( array $services ): array {
		$serviceData = array();
		foreach ( $services as $service ) {
			$serviceObj = Service::find( $service->service_id );
			if ( ! $serviceObj ) {
				continue;
			}

			$getCategory   = Category::find( $serviceObj->category_id );
			$category      = array();
			if(!empty($getCategory)){
				$category      = array(
					'id'   => $getCategory->id,
					'name' => $getCategory->name,
				);
			}
			$listing_id  = ! empty( $serviceObj->listing_id ) ? (int) $serviceObj->listing_id : null;
			$listing_url = null;
			if ( $listing_id && 'classified_listing' === ( $serviceObj->source_type ?? 'manual' ) ) {
				$permalink = get_permalink( $listing_id );
				if ( $permalink ) {
					$listing_url = $permalink;
				}
			}
			$serviceData[] = array(
				'id'                     => $service->id,
				'user_id'                => (int) $service->user_id,
				'service_id'             => $serviceObj->id,
				'name'                   => $serviceObj->name,
				'category'               => $category,
				'price'                  => $service->price,
				'service_price'          => $serviceObj->price,
				'min_capacity'           => $service->min_capacity,
				'max_capacity'           => $service->max_capacity,
				'custom_pricing'         => maybe_unserialize( $service->custom_pricing ),
				'buffer_after'           => $serviceObj->get_buffer_after(),
				'buffer_before'          => $serviceObj->get_buffer_before(),
				'booking_advance_period' => $serviceObj->get_booking_advance_period(),
				'source_type'            => $serviceObj->source_type ?? 'manual',
				'listing_id'             => $listing_id,
				'listing_url'            => $listing_url,
			);
		}

		return $serviceData;
	}

	/**
	 * Deletes a specific EmployeeService record identified by user ID and delete ID.
	 *
	 * This method locates the EmployeeService entry based on the provided IDs
	 * and deletes it if found.
	 *
	 * @param int $user_id The ID of the user associated with the EmployeeService.
	 * @param int $delete_id The ID of the EmployeeService record to be deleted.
	 *
	 * @return void
	 */
	public function deleteUserService( int $user_id, int $delete_id ): void {
		$employeeService = EmployeeService::where( 'user_id', $user_id )->where( 'id', $delete_id )->first();
		$employee        = EmployeeService::find( $employeeService->id );
		if ( $employee ) {
			$employee->delete();
		}
	}

	/**
	 *
	 * * Find or create an EmployeeDayoff instance.
	 *
	 * @param array $dayOff The day off data, which may include 'id'.
	 *
	 * @return EmployeeDayoff The found or newly created EmployeeDayoff instance.
	 * @throws \RuntimeException If the day off with the given ID does not exist.
	 */
	public function findOrCreateEmployeeDayOff( array $dayOff ): EmployeeDayoff {
		$employeeDayOff = isset( $dayOff['id'] )
			? EmployeeDayoff::findOrNew( $dayOff['id'] )
			: new EmployeeDayoff();

		if ( isset( $dayOff['id'] ) && ! $employeeDayOff->id ) {
			throw new \RuntimeException( 'Employee day off not found' );
		}

		return $employeeDayOff;
	}

	/**
	 * Update an existing employee day off record.
	 *
	 * This method updates the given EmployeeDayoff model instance with new values
	 * provided in the `$dayOff` array, including start date, end date, name, and recurring status.
	 *
	 * @param EmployeeDayoff $employeeDayOff The EmployeeDayoff model instance to update.
	 * @param int $userId The ID of the user associated with the day off.
	 * @param array $dayOff An associative array containing day off data:
	 *                                       - start_date (string|null)
	 *                                       - end_date (string|null)
	 *                                       - name (string|null)
	 *                                       - recurring (bool)
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function updateEmployeeDayOff( EmployeeDayoff $employeeDayOff, int $userId, array $dayOff ): void {
		$employeeDayOff->fill(
			array(
				'user_id'    => $userId,
				'start_date' => $dayOff['start_date'] ?? null,
				'end_date'   => $dayOff['end_date'] ?? null,
				'name'       => $dayOff['name'] ?? null,
				'type'       => $dayOff['type'] ?? null,
				'recurring'  => $dayOff['recurring'] ?? false,
			)
		);

		$employeeDayOff->save();
	}

	/**
	 * Retrieve and format the days off for a given user.
	 *
	 * This method fetches all associated day-off records for the specified user
	 * and returns them as a formatted array. If the user is not found or has no days off,
	 * an empty array is returned.
	 *
	 * @param int $userId The ID of the user whose days off should be retrieved.
	 *
	 * @return array The formatted list of the user's days off. Returns an empty array if none are found.
	 * @since 1.0.0
	 */
	public function getUserDaysOff( int $userId ): array {
		$user = $this->findById( $userId );
		if ( ! $user ) {
			return array();
		}

		$daysOff = $user->daysOff()->get();
		if ( empty( $daysOff ) ) {
			return array();
		}

		return $this->formatUserDaysOff( $daysOff );
	}

	/**
	 * Format and structure user days off data into an array.
	 *
	 * This method processes an array of day off objects and reformats the data
	 * into a structured associative array for each day off.
	 *
	 * @param array $daysOff An array of day off objects to format. Each object is expected to have properties such as id, user_id, name, start_date, end_date, type, and recurring.
	 *
	 * @return array A reformatted array of user days off data, where each day off is an associative array containing id, user_id, name, start_date, end_date, type, and recurring.
	 */
	public function formatUserDaysOff( array $daysOff ): array {
		$result = array();

		foreach ( $daysOff as $dayOff ) {
			$result[] = array(
				'id'         => $dayOff->id,
				'user_id'    => $dayOff->user_id,
				'name'       => $dayOff->name,
				'start_date' => $dayOff->start_date,
				'end_date'   => $dayOff->end_date,
				'type'       => $dayOff->type ?? '',
				'recurring'  => $dayOff->recurring,
			);
		}

		return $result;
	}

	/**
	 * Delete a user's day off by ID.
	 *
	 * @param int $user_id The ID of the user.
	 * @param int $delete_id The ID of the day off to delete.
	 *
	 * @return void
	 */
	public function deleteUserDayOff( int $user_id, int $delete_id ): void {
		$employeeDayOff = EmployeeDayoff::where( 'user_id', $user_id )
										->where( 'id', $delete_id )
										->first();
		$employeeDayOff = EmployeeDayoff::find( $employeeDayOff->id );
		if ( $employeeDayOff ) {
			$employeeDayOff->delete();
		}
	}

	/**
	 * Find an existing EmployeeWeekday by ID or create a new instance.
	 *
	 * If an ID is provided in the `$weekday` array, the method attempts to find the corresponding
	 * EmployeeWeekday model. If not found, it throws a RuntimeException. If no ID is provided,
	 * a new instance is returned.
	 *
	 * @param array $weekday Associative array of weekday data. If 'id' is present, attempts to find the model.
	 *
	 * @return EmployeeWeekday The found or newly created EmployeeWeekday model instance.
	 * @throws \RuntimeException If the provided ID is set but the EmployeeWeekday is not found.
	 *
	 * @since 1.0.0
	 */
	public function findOrCreateEmployeeWeekday( array $weekday ): EmployeeWeekday {
		$employeeWeekday = isset( $weekday['id'] )
			? EmployeeWeekday::findOrNew( $weekday['id'] )
			: new EmployeeWeekday();

		if ( isset( $weekday['id'] ) && ! $employeeWeekday->id ) {
			throw new \RuntimeException( 'Employee weekday not found' );
		}

		return $employeeWeekday;
	}

	/**
	 * Update an existing EmployeeWeekday record with new values.
	 *
	 * This method updates the given EmployeeWeekday model instance with values provided
	 * in the `$weekday` array. It includes fields such as day index, start time, end time,
	 * and status, and assigns the record to the given user ID.
	 *
	 * @param EmployeeWeekday $employeeWeekday The EmployeeWeekday model instance to update.
	 * @param int $userId The ID of the user associated with the weekday.
	 * @param array $weekday Associative array containing weekday data:
	 *                                         - day_index (int|null)
	 *                                         - start_time (string|null)
	 *                                         - end_time (string|null)
	 *                                         - status (int, defaults to 1)
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function updateEmployeeWeekday( EmployeeWeekday $employeeWeekday, int $userId, array $weekday ): void {
		$employeeWeekday->fill(
			array(
				'user_id'    => $userId,
				'day_index'  => $weekday['day_index'] ?? null,
				'start_time' => $weekday['start_time'] ?? null,
				'end_time'   => $weekday['end_time'] ?? null,
				'status'     => $weekday['status'] ?? 1,
			)
		);

		$employeeWeekday->save();
	}

	/**
	 * Find an EmployeeWeekday by user ID and weekday ID.
	 *
	 * This method looks up an EmployeeWeekday record that matches the given user ID and weekday ID.
	 * If `$loadObject` is `true`, it performs a second `find()` query using the ID of the found record
	 * (useful if you want to load relationships or a fresh instance). If no record is found, it returns null.
	 *
	 * @param int $user_id The ID of the user associated with the weekday.
	 * @param int $weekday_id The ID of the weekday record to retrieve.
	 * @param bool $loadObject Whether to return a reloaded model instance via `find()` (default: false).
	 *
	 * @return EmployeeWeekday|null The found EmployeeWeekday instance or null if not found.
	 * @since 1.0.0
	 */
	public function findEmployeeWeekdayById( int $user_id, int $weekday_id, $loadObject = false ): EmployeeWeekday {
		$employeeWeekday = EmployeeWeekday::where( 'user_id', $user_id )
										  ->where( 'id', $weekday_id )
										  ->first();
		if ( $loadObject ) {
			return EmployeeWeekday::find( $employeeWeekday->id );
		} else {
			return $employeeWeekday;
		}
	}

	/**
	 * Retrieve all break times for a given EmployeeWeekday.
	 *
	 * This method finds the EmployeeWeekday by its ID and returns the related break times.
	 *
	 * @param int $employeeWeekdayId The ID of the EmployeeWeekday.
	 *
	 * @since 1.0.0
	 */
	public function getBreakTimes( $employeeWeekdayId ) {
		$employeeWeekday = EmployeeWeekday::find( $employeeWeekdayId );

		return $employeeWeekday->employeeBreakTimes()->get();
	}

	/**
	 * Retrieve all time periods for a given EmployeeWeekday.
	 *
	 * This method finds the EmployeeWeekday by its ID and returns the related time periods.
	 *
	 * @param int $employeeWeekdayId The ID of the EmployeeWeekday.
	 *
	 * @return array The collection of related EmployeeWeekdayTimePeriod records.
	 * @since 1.0.0
	 */
	public function getTimePeriods( $employeeWeekdayId ) {
		$timePeriods = EmployeeWeekday::find( $employeeWeekdayId );

		return $timePeriods->employeeTimePeriods()->get();
	}

	/**
	 * Delete multiple EmployeeWeekdayBreakTime records by their IDs.
	 *
	 * This method accepts either an array of IDs or an array of associative arrays
	 * containing an 'id' key, extracts the IDs, and performs a force delete.
	 *
	 * @param array $timeBreaks Array of break time IDs or arrays containing 'id' keys.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function deleteBreakTimes( $timeBreaks ) {
		$timeBreaksIds = array_column( $timeBreaks, 'id' );
		$timeBreaksIds = empty( $timeBreaksIds ) ? (array) $timeBreaks : $timeBreaksIds;
		if ( ! empty( $timeBreaksIds ) ) {
			EmployeeWeekdayBreakTime::forceDeleteMany( $timeBreaksIds );
		}
	}

	/**
	 * Force delete multiple EmployeeWeekdayBreakTime records by their IDs.
	 *
	 * This method deletes the specified break time records permanently using their IDs.
	 *
	 * @param array $timeBreaksIds An array of break time record IDs to delete.
	 *
	 * @return bool True on success, false on failure.
	 * @since 1.0.0
	 */
	public function deleteBreakTimesByIds( $timeBreaksIds ) {
		return EmployeeWeekdayBreakTime::forceDeleteMany( $timeBreaksIds );
	}

	/**
	 * Delete all break times associated with a specific EmployeeWeekday.
	 *
	 * This method retrieves all break times for the given weekday ID and deletes them.
	 *
	 * @param int $weekdayId The ID of the EmployeeWeekday.
	 *
	 * @return void|array
	 * @since 1.0.0
	 */
	public function deleteBreakTimesByWeekday( $weekdayId ) {
		$timeBreaks = $this->getBreakTimes( $weekdayId );

		return $this->deleteBreakTimes( $timeBreaks );
	}

	/**
	 * Retrieve locations associated with given weekday time period IDs.
	 *
	 * @param array $weekdayTimePeriodIds An array of weekday time period IDs.
	 *
	 * @return array The collection of matching EmployeeWeekdayTimePeriodLocation records.
	 * @since 1.0.0
	 */
	public function getTimePeriodLocations( $weekdayTimePeriodIds ) {
		if ( empty( $weekdayTimePeriodIds ) ) {
			return array();
		}

		return EmployeeWeekdayTimePeriodLocation::whereIn( 'weekday_time_period_id', $weekdayTimePeriodIds )->get();
	}

	/**
	 * Delete EmployeeWeekdayTimePeriodLocation records by given time period IDs.
	 *
	 * This method retrieves all time period locations associated with the provided IDs
	 * and force deletes them if any are found.
	 *
	 * @param array $timePeriodsIds An array of EmployeeWeekdayTimePeriod IDs.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function deleteTimePeriodLocations( $timePeriodsIds ) {
		if ( empty( $timePeriodsIds ) ) {
			return;
		}
		$timePeriodLocations = $this->getTimePeriodLocations( $timePeriodsIds );

		if ( ! empty( $timePeriodLocations ) ) {
			EmployeeWeekdayTimePeriodLocation::forceDeleteMany( ( new Collection( $timePeriodLocations ) )->pluck( 'id' ) );
		}
	}

	/**
	 * Force delete multiple EmployeeWeekdayTimePeriod records by their IDs.
	 *
	 * @param array $timePeriodsIds Array of EmployeeWeekdayTimePeriod IDs to delete.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function deleteTimePeriods( $timePeriodsIds ) {
		EmployeeWeekdayTimePeriod::forceDeleteMany( $timePeriodsIds );
	}

	/**
	 * Retrieve services associated with the given weekday time period IDs.
	 *
	 * @param array $timePeriodsIds Array of weekday time period IDs.
	 *
	 * @return array Collection of EmployeeWeekdayTimePeriodService records.
	 * @since 1.0.0
	 */
	public function getTimePeriodServices( $timePeriodsIds ) {
		if ( empty( $timePeriodsIds ) ) {
			return array();
		}

		return EmployeeWeekdayTimePeriodService::whereIn( 'weekday_time_period_id', $timePeriodsIds )->get();
	}

	/**
	 * Delete EmployeeWeekdayTimePeriodService records by given time period IDs.
	 *
	 * This method retrieves all time period services associated with the provided IDs
	 * and force deletes them if any are found.
	 *
	 * @param array $timePeriodsIds An array of EmployeeWeekdayTimePeriod IDs.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function deleteTimePeriodServices( $timePeriodsIds ) {
		if ( empty( $timePeriodsIds ) ) {
			return;
		}
		$timePeriodServices = $this->getTimePeriodServices( $timePeriodsIds );
		if ( ! empty( $timePeriodServices ) ) {
			EmployeeWeekdayTimePeriodService::forceDeleteMany( ( new Collection( $timePeriodServices ) )->pluck( 'id' ) );
		}
	}

	/**
	 * Delete EmployeeWeekdayTimePeriodService records for a specific time period ID
	 * except those with IDs in the provided service IDs list.
	 *
	 * @param int $timePeriodsIds Thi
	 * @param array $serviceIds Array of service IDs to keep (not delete).
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function deleteTimePeriodServicesNotInList( $timePeriodsIds, $serviceIds ) {
		$existingServices = EmployeeWeekdayTimePeriodService::where( 'weekday_time_period_id', $timePeriodsIds )
															->whereNotIn( 'id', $serviceIds )
															->get();

		$existingServiceIds = array_column( $existingServices, 'id' );
		$deleteIds          = array_diff( $existingServiceIds, $serviceIds );

		if ( ! empty( $deleteIds ) ) {
			EmployeeWeekdayTimePeriodService::forceDeleteMany( $deleteIds );
		}
	}

	/**
	 * Delete all break times associated with a specific weekday ID.
	 *
	 * This method retrieves all EmployeeWeekdayBreakTime records for the given weekday
	 * and deletes them using the `deleteBreakTimes` method.
	 *
	 * @param int $weekday_id The ID of the weekday.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function deleteAllBreaksForWeekday( $weekday_id ) {
		$existingBreaks = EmployeeWeekdayBreakTime::where( 'weekday_id', $weekday_id )->get();
		$this->deleteBreakTimes( $existingBreaks );
	}

	/**
	 * Delete all time period locations for a given time period ID.
	 *
	 * This method fetches all EmployeeWeekdayTimePeriodLocation records linked to the given
	 * weekday time period ID and deletes them.
	 *
	 * @param int $time_period_id The ID of the weekday time period.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function deleteAllTimePeriodLocation( $time_period_id ) {
		$existingLocations = EmployeeWeekdayTimePeriodLocation::where( 'weekday_time_period_id', $time_period_id )
															  ->get();
		if ( empty( $existingLocations ) ) {
			return;
		}
		$weekday_time_period_id = ( new Collection( $existingLocations ) )->pluck( 'weekday_time_period_id' );
		$this->deleteTimePeriodLocations( $weekday_time_period_id );
	}

	/**
	 * Delete EmployeeWeekdayTimePeriodLocation records for a given time period ID,
	 * excluding those with IDs in the provided location IDs list.
	 *
	 * @param array $locations (Unused parameter — consider removing if unnecessary)
	 * @param int $time_period_id The weekday time period ID.
	 * @param array $locationIds Array of location IDs to keep (not delete).
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function deleteTimePeriodLocationNotInList( $locations, $time_period_id, $locationIds ) {

		$existingLocations = EmployeeWeekdayTimePeriodLocation::where( 'weekday_time_period_id', $time_period_id )
															  ->whereNotIn( 'id', $locationIds )
															  ->get();

		$existingLocationIds = ( new Collection( $existingLocations ) )->pluck( 'id' );
		$deleteIds           = array_diff( $existingLocationIds, $locationIds );

		if ( ! empty( $deleteIds ) ) {
			EmployeeWeekdayTimePeriodLocation::forceDeleteMany( $deleteIds );
		}
	}

	/**
	 * Delete all time period services associated with a given weekday time period ID.
	 *
	 * This method fetches all EmployeeWeekdayTimePeriodService records linked to the given
	 * time period ID and deletes them.
	 *
	 * @param int $time_period_id The ID of the weekday time period.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function deleteAllTimePeriodServices( $time_period_id ) {
		$existingServices = EmployeeWeekdayTimePeriodService::where( 'weekday_time_period_id', $time_period_id )->get();
		if ( empty( $existingServices ) ) {
			return;
		}
		$weekday_time_period_id = ( new Collection( $existingServices ) )->pluck( 'weekday_time_period_id' );
		$this->deleteTimePeriodServices( $weekday_time_period_id );
	}

	/**
	 * Delete EmployeeWeekdayBreakTime records for given weekdays excluding those in the keep list.
	 *
	 * @param array $breaks Array of break records (each containing 'weekday_id').
	 * @param int $weekday_id The ID of the weekday (not directly used in current logic, consider removing if redundant).
	 * @param array $keepIds Array of break time IDs to keep (not delete).
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function deleteBreaksNotInList( $breaks, $weekday_id, $keepIds ) {
		$weekdayIds = ( new Collection( $breaks ) )->pluck( 'weekday_id' );

		$existingBreaks = EmployeeWeekdayBreakTime::whereIn( 'weekday_id', $weekdayIds )
												  ->whereNotIn( 'id', $keepIds )
												  ->get();

		$existingBreakIds = ( new Collection( $existingBreaks ) )->pluck( 'id' );
		$deleteIds        = array_diff( $existingBreakIds, $keepIds );

		if ( ! empty( $deleteIds ) ) {
			$this->deleteBreakTimes( $deleteIds );
		}
	}

	/**
	 * Create or update an EmployeeWeekdayBreakTime record.
	 *
	 * If an 'id' is present in `$breakData`, this method attempts to find the existing record
	 * and update it. Otherwise, it creates a new record. It fills the record with provided data
	 * and saves it.
	 *
	 * @param int $user_id The ID of the user associated with the break time.
	 * @param int $weekday_id The ID of the weekday associated with the break time.
	 * @param array $breakData Associative array with break time data:
	 *                          - id (optional) The break time record ID to update.
	 *                          - start_time (string) The break start time.
	 *                          - end_time (string) The break end time.
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function saveBreakTime( $user_id, $weekday_id, $breakData ) {
		$employeeBreak = isset( $breakData['id'] )
			? EmployeeWeekdayBreakTime::findOrNew( $breakData['id'] )
			: new EmployeeWeekdayBreakTime();
		$employeeBreak->fill(
			array(
				'user_id'    => $user_id,
				'weekday_id' => $weekday_id,
				'start_time' => $breakData['start_time'],
				'end_time'   => $breakData['end_time'],
				'note'       => $breakData['note'] ?? '',
			)
		);

		$employeeBreak->save();
	}

	/**
	 * Retrieve all weekdays associated with a specific user.
	 *
	 * This method checks if the user exists, and if so, fetches the user's weekdays
	 * via the UserWeekdayDataService. Returns an empty array if the user does not exist.
	 *
	 * @param int $user_id The ID of the user.
	 *
	 * @return array An array of the user's weekdays, or an empty array if user not found.
	 * @since 1.0.0
	 */
	public function getUserWeekdays( $user_id ) {
		$user = User::find( $user_id );
		if ( ! $user ) {
			return array();
		}

		return UserWeekdayDataService::getUserWeekdays( $user_id );
	}


	/**
	 * Retrieve the special days associated with a specific user.
	 *
	 * This method fetches and formats the special days (e.g., holidays, important dates)
	 * linked to the provided user ID.
	 *
	 * @param int $userId The ID of the user whose special days are to be retrieved.
	 *
	 * @return array An array of formatted special days. Returns an empty array if the user is not found or has no special days.
	 */
	public function getUserSpecialDays( $userId ): array {
		$user = User::find( $userId );
		if ( ! $user ) {
			return array();
		}

		$specialDays = $user->specialDays()->get();
		if ( empty( $specialDays ) ) {
			return array();
		}

		return $this->formatUserSpecialDays( $specialDays );
	}

	/**
	 * Format user special days into a structured array.
	 *
	 * @param array $specialDays An array of EmployeeSpecialDay models.
	 *
	 * @return array An array of formatted special day data.
	 */
	public function formatUserSpecialDays( array $specialDays ): array {
		$specialDayData = array();
		foreach ( $specialDays as $specialDay ) {
			$specialDayData[] = array(
				'id'           => $specialDay->id,
				'user_id'      => $specialDay->user_id,
				'name'         => $specialDay->name,
				'start_date'   => $specialDay->start_date,
				'end_date'     => $specialDay->end_date,
				'time_periods' => $this->getSpecialDayTimePeriods( $specialDay->id ),
			);
		}

		return $specialDayData;
	}

	/**
	 * Find an existing EmployeeSpecialDay by ID or create a new instance.
	 *
	 * This method attempts to retrieve an EmployeeSpecialDay record by its ID. If the record does not exist,
	 * a new EmployeeSpecialDay instance is returned. If the provided ID does not correspond to an existing
	 * record, an exception is thrown.
	 *
	 * @param array $specialDay An associative array containing the special day details, including an optional 'id' key.
	 *
	 * @return EmployeeSpecialDay The found or newly created EmployeeSpecialDay instance.
	 *
	 * @throws \RuntimeException If the ID is provided but the related EmployeeSpecialDay is not found.
	 */
	public function findOrNewSpecialDay( $specialDay ): EmployeeSpecialDay {
		if ( isset( $specialDay['id'] ) ) {
			$employeeSpecialDay = EmployeeSpecialDay::findOrNew( $specialDay['id'] );
			if ( ! $employeeSpecialDay->id ) {
				throw new \RuntimeException( 'Employee weekday not found' );
			}

			return $employeeSpecialDay;
		}

		return new EmployeeSpecialDay();
	}

	/**
	 * Update the details of an existing EmployeeSpecialDay record.
	 *
	 * This method updates the EmployeeSpecialDay record with new data, including user ID, start date, end date, and name.
	 *
	 * @param EmployeeSpecialDay $employeeSpecialDay The instance of the EmployeeSpecialDay to update.
	 * @param int $userId The ID of the user associated with the special day.
	 * @param array $specialDay An associative array containing the special day data with keys: 'start_date', 'end_date', and 'name'.
	 *
	 * @return void
	 */
	public function updateSpecialDay( EmployeeSpecialDay $employeeSpecialDay, int $userId, array $specialDay ): void {
		$employeeSpecialDay->fill(
			array(
				'user_id'    => $userId,
				'start_date' => $specialDay['start_date'] ?? null,
				'end_date'   => $specialDay['end_date'] ?? null,
				'name'       => $specialDay['name'] ?? null,
			)
		);
		$employeeSpecialDay->save();
	}

	/**
	 * Find an EmployeeSpecialDay by user ID and special day ID.
	 *
	 * This method retrieves an EmployeeSpecialDay record that matches the given user ID
	 * and special day ID. If `$loadObject` is `true`, it performs a second `find()` query
	 * using the ID of the found record (useful if you want to load relationships or a fresh instance).
	 * If no record is found, it returns null.
	 *
	 * @param int $user_id The ID of the user associated with the special day.
	 * @param int $special_day_id The ID of the special day record to retrieve.
	 * @param bool $loadObject Whether to return a reloaded model instance via `find()` (default: false).
	 *
	 * @return EmployeeSpecialDay|null The found EmployeeSpecialDay instance or null if not found.
	 * @since 1.0.0
	 */
	public function findSpecialDayById( int $user_id, int $special_day_id, $loadObject = false ): EmployeeSpecialDay {
		$employeeSpecialDay = EmployeeSpecialDay::where( 'user_id', $user_id )
												->where( 'id', $special_day_id )
												->first();
		if ( $loadObject ) {
			return EmployeeSpecialDay::find( $employeeSpecialDay->id );
		} else {
			return $employeeSpecialDay;
		}
	}

	/**
	 * Delete a specific EmployeeSpecialDay record.
	 *
	 * This method deletes a special day record based on the given data if the special day ID exists.
	 *
	 * @param array $specialDay An associative array containing the special day data, including its ID.
	 *
	 * @return void
	 *
	 * @throws \InvalidArgumentException If the special day ID is not provided.
	 */
	public function deleteSpecialDay( $specialDay ) {
		if ( isset( $specialDay['id'] ) ) {
			$employeeSpecialDay = EmployeeSpecialDay::find( $specialDay['id'] );
			if ( $employeeSpecialDay ) {
				$employeeSpecialDay->delete();
			}
		} else {
			throw new \InvalidArgumentException( 'Special day ID is required for deletion' );
		}
	}

	/**
	 * Find an existing EmployeeSpecialDayTimePeriod by ID or create a new instance.
	 *
	 * This method attempts to find an EmployeeSpecialDayTimePeriod record using the provided ID.
	 * If the ID is not set or the record does not exist, it creates a new instance.
	 * Throws an exception if the record is not found for the provided ID.
	 *
	 * @param array $period An array containing the 'id' key to identify the period or empty for creating a new instance.
	 *
	 * @return EmployeeSpecialDayTimePeriod An existing or newly created EmployeeSpecialDayTimePeriod instance.
	 *
	 * @throws \RuntimeException If the record with the specified ID is not found.
	 */
	public function findOrNewSpecialDayTimePeriod( $period ): EmployeeSpecialDayTimePeriod {
		if ( isset( $period['id'] ) ) {
			$employeeSpecialTimePeriod = EmployeeSpecialDayTimePeriod::findOrNew( $period['id'] );
			if ( ! $employeeSpecialTimePeriod->id ) {
				throw new \RuntimeException( 'Employee weekday time period not found' );
			}

			return $employeeSpecialTimePeriod;
		}

		return new EmployeeSpecialDayTimePeriod();
	}

	/**
	 * Updates the specified EmployeeSpecialDayTimePeriod with the provided details.
	 *
	 * This method assigns the given special day ID and time period values to the EmployeeSpecialDayTimePeriod
	 * object and saves the changes to the database.
	 *
	 * @param EmployeeSpecialDayTimePeriod $employeeTimePeriod The EmployeeSpecialDayTimePeriod instance to update.
	 * @param int $special_day_id The ID of the special day.
	 * @param array $period An associative array containing 'start_time' and 'end_time' values for the time period.
	 *
	 * @return void
	 */
	public function updateSpecialDayTimePeriod( EmployeeSpecialDayTimePeriod $employeeTimePeriod, int $special_day_id, array $period ): void {
		$employeeTimePeriod->fill(
			array(
				'special_day_id' => $special_day_id,
				'start_time'     => $period['start_time'] ?? null,
				'end_time'       => $period['end_time'] ?? null,
			)
		);
		$employeeTimePeriod->save();
	}

	/**
	 * Retrieve time periods for a specific special day.
	 *
	 * This method fetches all time periods associated with the given special day ID
	 * and formats them using the defined internal logic.
	 *
	 * @param int $specialDayId The ID of the special day for which time periods need to be retrieved.
	 *
	 * @return array An array of formatted time periods. Returns an empty array if no periods are found.
	 */
	public function getSpecialDayTimePeriods( $specialDayId ): array {
		$employeeSpecialDay = EmployeeSpecialDayTimePeriod::where( 'special_day_id', $specialDayId )->get();
		if ( empty( $employeeSpecialDay ) ) {
			return array();
		}

		return $this->formateSpecialDayTimePeriods( $employeeSpecialDay );
	}

	/**
	 * Format special day time periods with additional details.
	 *
	 * This method processes an array of special day time period objects and formats them
	 * into an array with relevant details, including associated locations and services.
	 *
	 * @param array $specialDayTimePeriods An array of special day time period objects to format.
	 *
	 * @return array An array of formatted time period data with associated details.
	 */
	public function formateSpecialDayTimePeriods( array $specialDayTimePeriods ): array {
		$timePeriodData = array();
		foreach ( $specialDayTimePeriods as $timePeriod ) {
			$timePeriodData[] = array(
				'id'             => $timePeriod->id,
				'special_day_id' => $timePeriod->special_day_id,
				'start_time'     => $timePeriod->start_time,
				'end_time'       => $timePeriod->end_time,
				'locations'      => $this->getSpecialDayTimePeriodLocations( $timePeriod->id ),
				'services'       => $this->getSpecialDayTimePeriodServices( $timePeriod->id ),
			);
		}

		return $timePeriodData;
	}

	/**
	 * Find or create a new EmployeeSpecialDayTimePeriodLocation record.
	 *
	 * This method attempts to find an existing record using the provided ID
	 * within the special day time period location data. If the ID exists but
	 * no corresponding record is found, an exception is thrown. If no ID is
	 * provided, a new instance of EmployeeSpecialDayTimePeriodLocation is returned.
	 *
	 * @param array $specialDayTimePeriodLocation An associative array containing
	 *                                            data for the special day time
	 *                                            period location, including an
	 *                                            optional 'id' key.
	 *
	 * @return EmployeeSpecialDayTimePeriodLocation The located or newly created
	 *                                               instance of EmployeeSpecialDayTimePeriodLocation.
	 *
	 * @throws \RuntimeException If an ID is provided and the corresponding record
	 *                           does not exist.
	 */
	public function findOrNewSpecialDayTimePeriodLocations( $specialDayTimePeriodLocation ): EmployeeSpecialDayTimePeriodLocation {
		if ( isset( $specialDayTimePeriodLocation['id'] ) ) {
			$employeeTimePeriodLocation = EmployeeSpecialDayTimePeriodLocation::findOrNew( $specialDayTimePeriodLocation['id'] );
			if ( ! $employeeTimePeriodLocation->id ) {
				throw new \RuntimeException( 'Employee weekday time period location not found' );
			}

			return $employeeTimePeriodLocation;
		}

		return new EmployeeSpecialDayTimePeriodLocation();
	}


	/**
	 * Update the locations for a special time period of an employee.
	 *
	 * This method updates the specified special time period location record with the provided special day ID
	 * and location data, and saves the changes.
	 *
	 * @param EmployeeSpecialDayTimePeriodLocation $employeeTimePeriodLocation The special day time period location record to update.
	 * @param int $specialDayId The ID of the special day associated with the time period.
	 * @param array $locations An array containing location data, including an optional 'location_id'.
	 *
	 * @return void
	 */
	public function updateSpecialTimePeriodLocations( EmployeeSpecialDayTimePeriodLocation $employeeTimePeriodLocation, int $specialDayId, array $locations ): void {
		$employeeTimePeriodLocation->fill(
			array(
				'special_day_time_period_id' => $specialDayId,
				'location_id'                => $locations['location_id'] ?? null,
			)
		);

		$employeeTimePeriodLocation->save();
	}

	/**
	 * Retrieve formatted locations for a special day's time period.
	 *
	 * This method fetches the locations associated with the given special day time period ID
	 * and formats them for return.
	 *
	 * @param int $specialDayTimePeriodId The ID of the special day time period to retrieve locations for.
	 *
	 * @return array An array of formatted locations for the specified time period. Returns an empty array if the time period is not found.
	 */
	public function getSpecialDayTimePeriodLocations( $specialDayTimePeriodId ): array {
		$employeeSpecialDayTimePeriod = EmployeeSpecialDayTimePeriod::find( $specialDayTimePeriodId );
		if ( ! $employeeSpecialDayTimePeriod ) {
			return array();
		}

		return $this->formateSpecialDayTimePeriodLocations( $employeeSpecialDayTimePeriod->getTimePeriodLocations()->get() );
	}

	/**
	 * Format special day time period locations into a structured array.
	 *
	 * This method processes an array of special day time period location objects
	 * and formats them into an array with specific keys and corresponding data.
	 *
	 * @param array $specialDayTimePeriodLocations An array of objects representing special day time period locations.
	 *
	 * @return array An array of formatted special day time period location data.
	 */
	public function formateSpecialDayTimePeriodLocations( array $specialDayTimePeriodLocations ): array {
		$locationData = array();
		foreach ( $specialDayTimePeriodLocations as $location ) {
			$locationData[] = array(
				'id'                         => $location->id,
				'special_day_time_period_id' => $location->special_day_time_period_id,
				'location_id'                => $location->location_id,
			);
		}

		return $locationData;
	}

	/**
	 * Retrieve an existing EmployeeSpecialDayTimePeriodService or create a new instance.
	 *
	 * This method attempts to find an EmployeeSpecialDayTimePeriodService by its ID from the given service data.
	 * If the service is not found, a RuntimeException is thrown. If no ID is provided, a new instance is created and returned.
	 *
	 * @param array $service An associative array containing service data, including an optional 'id' key.
	 *
	 * @return EmployeeSpecialDayTimePeriodService The found or newly created instance of EmployeeSpecialDayTimePeriodService.
	 *
	 * @throws \RuntimeException If the service ID is provided but no matching record is found.
	 */
	public function findOrNewSpecialDayTimePeriodService( $service ): EmployeeSpecialDayTimePeriodService {
		if ( isset( $service['id'] ) ) {
			$employeeSpecialDayTimePeriodService = EmployeeSpecialDayTimePeriodService::findOrNew( $service['id'] );
			if ( ! $employeeSpecialDayTimePeriodService->id ) {
				throw new \RuntimeException( 'Employee weekday time period service not found' );
			}

			return $employeeSpecialDayTimePeriodService;
		}

		return new EmployeeSpecialDayTimePeriodService();
	}

	/**
	 * Updates the EmployeeSpecialDayTimePeriodService with the provided special day ID and service data.
	 *
	 * This method populates the EmployeeSpecialDayTimePeriodService entity with the given special day ID
	 * and service details, and then saves the updated entity.
	 *
	 * @param EmployeeSpecialDayTimePeriodService $employeeSpecialDayTimePeriodService The service entity to update.
	 * @param int $specialDayId The ID of the special day time period.
	 * @param array $service An associative array containing service details, including 'service_id'.
	 *
	 * @return void
	 */
	public function updateSpecialDayTimePeriodService( EmployeeSpecialDayTimePeriodService $employeeSpecialDayTimePeriodService, int $specialDayId, array $service ): void {
		$employeeSpecialDayTimePeriodService->fill(
			array(
				'special_day_time_period_id' => $specialDayId,
				'service_id'                 => $service['service_id'] ?? null,
			)
		);

		$employeeSpecialDayTimePeriodService->save();
	}

	/**
	 * Retrieve and format services for a specific special day time period.
	 *
	 * This method fetches the services associated with a specific special day time period
	 * and formats them for further processing.
	 *
	 * @param int $specialDayTimePeriodId The ID of the special day time period.
	 *
	 * @return array An array of formatted services for the specified special day time period.
	 */
	public function getSpecialDayTimePeriodServices( $specialDayTimePeriodId ): array {
		$employeeSpecialDayTimePeriod = EmployeeSpecialDayTimePeriod::find( $specialDayTimePeriodId );
		if ( ! $employeeSpecialDayTimePeriod ) {
			return array();
		}

		return $this->formateSpecialDayTimePeriodServices( $employeeSpecialDayTimePeriod->getTimePeriodServices()->get() );
	}

	/**
	 * Format special day time period services into structured data.
	 *
	 * This method processes an array of special day time period service objects
	 * and converts them into an array of associative arrays with specific keys.
	 *
	 * @param array $specialDayTimePeriodServices An array of special day time period service objects to format.
	 *
	 * @return array An array of formatted associative arrays containing service data.
	 */
	public function formateSpecialDayTimePeriodServices( array $specialDayTimePeriodServices ): array {
		$serviceData = array();
		foreach ( $specialDayTimePeriodServices as $service ) {
			$serviceData[] = array(
				'id'                         => $service->id,
				'special_day_time_period_id' => $service->special_day_time_period_id,
				'service_id'                 => $service->service_id,
			);
		}

		return $serviceData;
	}

	/**
	 * Retrieve a list of employees with their associated profile information.
	 *
	 * This method queries the database to fetch user records that are identified as employees,
	 * along with their email, first name, and last name from the related user profile.
	 *
	 * @return array A collection of employee records with their profile details.
	 */
	public function getEmployees() {
		$employees = User::query();
		$employees->leftJoin( 'radius_booking_user_profiles', 'radius_booking_users.id', '=', 'radius_booking_user_profiles.user_id' );
		$employees->where( radius_table_prefix() . 'users.user_type', 'employee' );
		$employees->select( radius_table_prefix() . 'users.id', radius_table_prefix() . 'users.email', radius_table_prefix() . 'user_profiles.first_name', radius_table_prefix() . 'user_profiles.last_name' );

		return $employees->get();
	}
}
