<?php

namespace HydraBooking\Admin\Controller;

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

use HydraBooking\DB\Booking;
use HydraBooking\DB\Meeting;
use HydraBooking\DB\Host;


class DateTimeController extends \DateTimeZone
{
	public function TimeZone()
	{
		// Include backward-compatible identifiers so browser-reported aliases
		// (for example Europe/Kiev or Asia/Calcutta) can still be matched.
		$time_zone_data = $this->listIdentifiers(\DateTimeZone::ALL_WITH_BC);
		$time_zone      = array();
		// make array in this format { value: 'New York', name: 'NY' },

		foreach ($time_zone_data as $key => $value) {
			$time_zone[] = array(
				'value' => $value,
				'name'  => $value,
			);
		}
		return $time_zone;
	}

	// Get Current Time Zone
	public function getCurrentTimeZone()
	{
		//  get current time zone based on current location
		// Get the user's current local time zone
		// Get the current time zone
		$currentTimeZone = new \DateTimeZone(date_default_timezone_get());

		// Get the time zone name
		$timeZoneName = $currentTimeZone->getName();

		// Output the time zone name
		echo $timeZoneName;
	}

	public function convert_time_based_on_timezone($meeting_date,  $time, $time_zone, $selected_time_zone, $time_format = '')
	{

		if ($meeting_date != '') {
			$time = new \DateTime($meeting_date . ' ' . $time, new \DateTimeZone($time_zone));
		} else {
			$time = new \DateTime($time, new \DateTimeZone($time_zone));
		}

		$time->setTimezone(new \DateTimeZone($selected_time_zone));

		if ($time_format == '12') {
			return $time->format('h:i A');
		}
		if ($time_format == '24') {
			return $time->format('H:i');
		}


		return $time;
	}




	public function convert_full_start_end_host_timezone_with_date($start_time, $end_time, $time_zone, $selected_time_zone,  $selected_date, $type)
	{



		$_tfhb_general_settings = get_option('_tfhb_general_settings');
		$time_format 		 = isset($_tfhb_general_settings['time_format']) ? $_tfhb_general_settings['time_format'] : '12';

		// Get date format from settings via Helper
		$helper = new Helper();
		$date_format = $helper->get_date_format_from_settings('M d, Y');

		$start_time = new \DateTime($selected_date . ' ' . $start_time, new \DateTimeZone($time_zone));
		$end_time   = new \DateTime($selected_date . ' ' . $end_time, new \DateTimeZone($time_zone));

		$start_time->setTimezone(new \DateTimeZone($selected_time_zone));

		$end_time->setTimezone(new \DateTimeZone($selected_time_zone));

		// Prepare formatted output
		if ($time_format == '12') {
			// Return in this format using settings-based date format: M d, Y 11:30 AM - 12:00 PM (America/New_York)
			$start_format = $start_time->format($date_format . ' g:i A');
			$end_format = $end_time->format('g:i A');
		} else {
			// Return in this format using settings-based date format: M d, Y 11:30 - 12:00 (America/New_York)
			$start_format = $start_time->format($date_format . ' H:i');
			$end_format = $end_time->format('H:i');
		}

		if ($type == 'start') {
			return $start_format . ' (' . $selected_time_zone . ')';
		} elseif ($type == 'full') {

			// Return formatted time range with the time zone name using settings-based date format
			return $start_format . ' - ' . $end_format . ' (' . $selected_time_zone . ')';
		}
	}
	public function getAvailableTimeData($meeting_id,  $selected_date, $selected_time_zone, $selected_time_format, $host_id = 0)
	{

		$meeting_id = isset($meeting_id) ? $meeting_id : '';
		$host_id = isset($host_id) ? $host_id : 0;

		$selected_date = isset($selected_date) ? sanitize_text_field($selected_date) : '';

		$selected_time_zone = isset($selected_time_zone) ? sanitize_text_field($selected_time_zone) : 'UTC';

		$selected_time_format = isset($selected_time_format) ? sanitize_text_field($selected_time_format) : '12';

		$meeting      = new Meeting();

		$MeetingsData = $meeting->get($meeting_id);

		// Meeting Information
		$data = get_post_meta($MeetingsData->post_id, '__tfhb_meeting_opt', true);


		$meeting_type      = isset($data['meeting_type']) ? $data['meeting_type'] : 'one-to-single';
		$max_book_per_slot = isset($data['max_book_per_slot']) ? $data['max_book_per_slot'] : 1;
		$is_display_max_book_slot = isset($data['is_display_max_book_slot']) ? $data['is_display_max_book_slot'] : 0;

		$availability_data = $this->GetAvailabilityData($MeetingsData);

		// Meeting time zone
		$time_zone = isset($availability_data['time_zone']) && !empty($availability_data['time_zone']) ? $availability_data['time_zone'] : 'UTC';

		// Disable Unavailable days
		$time_slots = isset($availability_data['time_slots']) ? $availability_data['time_slots'] : array();

		// Disable Unavailable days
		$date_slots = isset($availability_data['date_slots']) ? $availability_data['date_slots'] : array();

		// Duration
		$duration = isset($data['duration']) && ! empty($data['duration']) ? $data['duration'] : 30;

		if ($duration == 'custom') {
			$duration = isset($data['custom_duration']) && ! empty($data['custom_duration']) ? $data['custom_duration'] : $duration;
		}
		// Buffer Time Before
		$buffer_time_before = isset($data['buffer_time_before']) && ! empty($data['buffer_time_before']) ? $data['buffer_time_before'] : 0;

		// Buffer Time After
		$buffer_time_after = isset($data['buffer_time_after']) && ! empty($data['buffer_time_after']) ? $data['buffer_time_after'] : 0;

		// Meeting Interval
		$meeting_interval = isset($data['meeting_interval']) && ! empty($data['meeting_interval']) ? $data['meeting_interval'] : 0;

		// Disable Dates

		// Get All Booking Data.
		$booking = new Booking();

		// 
		$where = [
			'OR' => [
				['host_id', '=', $MeetingsData->host_id],
				['meeting_id', '=', $meeting_id]
			],
			['meeting_dates', '=', $selected_date],
			['status', '!=', 'canceled'],
			['status', '!=', 'hold'],
		];
		$bookings = $booking->getBookingWithAttendees(
			$where,
			null,
			'ASC'
		);




		$disabled_times = array();
		foreach ($bookings as $booking) {

			$meeting_dates = $booking->meeting_dates;
			$start_time    = $booking->start_time;
			$end_time      = $booking->end_time;
			// $time_zone     = $booking->attendee_time_zone;

			if ('one-to-group' == $meeting_type) {
				$attendees = $booking->attendees;

				if (count($attendees) != $max_book_per_slot) {
					continue;
				}
			}
			$meeting_dates_array = explode(',', $meeting_dates);
			// get the first date
			$meeting_date = $meeting_dates_array[0];

			$start_time_obj = $this->convert_time_based_on_timezone($meeting_date, $start_time, $time_zone, $selected_time_zone, '');
			$end_time_obj   = $this->convert_time_based_on_timezone($meeting_date, $end_time, $time_zone, $selected_time_zone, '');

			$start_time = $this->formatTime($start_time_obj, $selected_time_format, $selected_time_zone);
			$end_time   = $this->formatTime($end_time_obj, $selected_time_format, $selected_time_zone);

			$disabled_times[] = array(
				'start'        => $start_time,
				'end'          => $end_time,
				'start_object' => $start_time_obj,
				'end_object'   => $end_time_obj,
			);
			// echo $meeting_date;

		}

		// Fetch two-way sync busy slots from Google Calendar and merge them into disabled_times
		if (isset($MeetingsData->host_id)) {
			$google_calendar_service = new \HydraBooking\Services\Integrations\GoogleCalendar\GoogleCalendar();


			$gc_busy_slots = $google_calendar_service->getBusyTimeSlots($MeetingsData->host_id, $selected_date, $selected_time_zone, $selected_time_format);

			if (! empty($gc_busy_slots) && is_array($gc_busy_slots)) {
				$disabled_times = array_merge($disabled_times, $gc_busy_slots);
			}

			// Fetch two-way sync busy slots from Outlook Calendar
			if (class_exists('\HydraBookingPro\Services\Integrations\OutlookCalendar\OutlookCalendar') && method_exists('\HydraBookingPro\Services\Integrations\OutlookCalendar\OutlookCalendar', 'getBusyTimeSlots')) {
				$outlook_calendar_service = new \HydraBookingPro\Services\Integrations\OutlookCalendar\OutlookCalendar();
				$oc_busy_slots = $outlook_calendar_service->getBusyTimeSlots($MeetingsData->host_id, $selected_date, $selected_time_zone, $selected_time_format);

				if (! empty($oc_busy_slots) && is_array($oc_busy_slots)) {
					$disabled_times = array_merge($disabled_times, $oc_busy_slots);
				}
			}
		}

		// Time Slot
		$time_slots_data = array();
		// get Selected Date day
		$selected_day = gmdate('l', strtotime($selected_date));

		// only get selected day time slot in single array using array finter
		$selected_available = array();
		foreach ($time_slots as $single) {
			if ($single['day'] == $selected_day) {
				$selected_available = $single;
			}
		}

		$times = $selected_available ? $selected_available['times'] : array();

		foreach ($date_slots as $key => $value) {

			// in strings 2024-06-12, 2024-06-13, 2024-06-14 has date
			if (strpos($value['date'], $selected_date) !== false && $value['available'] == false) {

				$times = $value['times'];
			}
		}

		foreach ($times as $key => $value) {

			$start_time = $value['start'];

			$end_time = $value['end'];

			$generatedSlots = $this->generateTimeSlots($start_time, $end_time, $duration, $meeting_interval, $buffer_time_before, $buffer_time_after, $selected_date, $selected_time_format, $time_zone, $selected_time_zone, $bookings, $max_book_per_slot, $is_display_max_book_slot);

			$time_slots_data = array_merge($time_slots_data, $generatedSlots);
		}

		// Skip time slots that overlap with any existing booking range.
		$slot_format = ('12' === $selected_time_format) ? 'Y-m-d h:i A' : 'Y-m-d H:i';
		$time_slots_data = array_filter(
			$time_slots_data,
			function ($slot) use ($disabled_times, $selected_date, $selected_time_zone, $slot_format, $buffer_time_before, $buffer_time_after) {
				$norm_date = str_replace('/', '-', $selected_date);
				$slot_start = \DateTime::createFromFormat(
					$slot_format,
					$norm_date . ' ' . $slot['start'],
					new \DateTimeZone($selected_time_zone)
				);
				$slot_end = \DateTime::createFromFormat(
					$slot_format,
					$norm_date . ' ' . $slot['end'],
					new \DateTimeZone($selected_time_zone)
				);

				if (! $slot_start || ! $slot_end) {
					return true;
				}

				foreach ($disabled_times as $value) {
					if (empty($value['start_object']) || empty($value['end_object'])) {
						continue;
					}

					$booked_start_with_gap = clone $value['start_object'];
					$booked_end_with_gap = clone $value['end_object'];

					// To enforce buffer times, we must expand the existing booking both ways.
					// A new meeting needs buffer_time_before prior to its start, and the existing meeting needs buffer_time_after.
					// So the total gap between them must be buffer_time_before + buffer_time_after.
					$total_buffer = (int) $buffer_time_before + (int) $buffer_time_after;
					if ($total_buffer > 0) {
						$booked_end_with_gap->modify('+' . $total_buffer . ' minutes');
						$booked_start_with_gap->modify('-' . $total_buffer . ' minutes');
					}

					// Skip any slot that overlaps [booked_start - total_buffer, booked_end + total_buffer].
					if ($slot_start < $booked_end_with_gap && $slot_end > $booked_start_with_gap) {
						return false;
					}
				}

				return true;
			}
		);
		// return $time_slots_data;
		$data = array();
		foreach ($time_slots_data as $key => $value) {
			// array filter and remove if date already exists
			$data[] = $value;
		}
		return $data;
	}


	public function generateTimeSlots($startTime, $endTime, $duration, $meeting_interval, $buffer_time_before, $buffer_time_after, $selected_date, $time_format, $time_zone, $selected_time_zone, $bookings, $max_book_per_slot, $is_display_max_book_slot)
	{
		$timeSlots = array();

		// Example value for buffer time before meeting start (replace with your actual setting)
		$_tfhb_general_settings = get_option('_tfhb_general_settings');
		$allowed_reschedule_before_meeting_start =   isset($_tfhb_general_settings['allowed_reschedule_before_meeting_start']) && !empty($_tfhb_general_settings['allowed_reschedule_before_meeting_start']) ? $_tfhb_general_settings['allowed_reschedule_before_meeting_start'] : 10;

		if (is_array($allowed_reschedule_before_meeting_start)) {
			$skip_before_meeting_start = $allowed_reschedule_before_meeting_start[0]['limit'];
			$skip_before_format = $allowed_reschedule_before_meeting_start[0]['times'];
		} else {
			$skip_before_meeting_start = $allowed_reschedule_before_meeting_start;
			$skip_before_format = 'minutes';
		}


		// Convert start and end times based on the selected timezone
		$start = $this->convert_time_based_on_timezone($selected_date, $startTime, $time_zone, $selected_time_zone, '');
		$end = $this->convert_time_based_on_timezone($selected_date, $endTime, $time_zone, $selected_time_zone, '');

		// Clone the start time for manipulation
		$current = clone $start;
		$before = clone $start;
		$after = clone $start;

		// Convert to seconds for easier manipulation
		$diff = $duration * 60; // Convert to seconds
		$before_diff = $buffer_time_before * 60; // Convert to seconds
		$after_diff = $buffer_time_after * 60; // Convert to seconds
		$meeting_interval = $meeting_interval * 60; // Convert to seconds
		$total_diff = $diff + $before_diff + $after_diff;

		// Loop through the time range

		while ($current < $end) {

			$slot_end_obj = (clone $current)->modify("+$diff seconds");
			if ($slot_end_obj > $end) {
				break;
			}

			$start_time = $this->formatTime($current, $time_format, $selected_time_zone);
			$end_time   = $this->formatTime($slot_end_obj, $time_format, $selected_time_zone);

			// if current time is passed then skip skip_before_meeting_start
			$current_minus_skip = (clone $current)->modify("-$skip_before_meeting_start $skip_before_format");
			if (new \DateTime('now', new \DateTimeZone($time_zone)) > $current_minus_skip) {
				if ($meeting_interval == 0) {
					$current->modify("+$total_diff seconds");
				} else {
					$current->modify("+$meeting_interval seconds");
				}

				continue;
			}
			$arg = array(
				'bookings' => $bookings,
				'max_book_per_slot' => $max_book_per_slot,
				'is_display_max_book_slot' => $is_display_max_book_slot,
				'selected_date' => $selected_date,
				'start_time' => $start_time,
				'end_time' => $end_time,
				'selected_time_zone' => $selected_time_zone
			);
			$timeSlots[] = apply_filters(
				'hydra_booking/generate_time_slots_with_remaining_slots',
				array(
					'start' => $start_time,
					'end'   => $end_time,
				),
				$arg
			);

			if ($meeting_interval == 0) {
				$current->modify("+$total_diff seconds");
			} else {
				$current->modify("+$meeting_interval seconds");
			}
		}

		return $timeSlots;
	}

	public function formatTime($dateTime, $timeFormat, $timeZone)
	{

		$dateTime->setTimezone(new \DateTimeZone($timeZone));

		$format = '';
		if ($timeFormat === '12') {
			$format = 'h:i A'; // 12-hour format with AM/PM
		} else {
			$format = 'H:i'; // 24-hour format
		}

		return $dateTime->format($format);
	}

	/* convert date time format */
	public function convertDateTimeFormat($date, $currentFormat, $newFormat)
	{
		$date = \DateTime::createFromFormat($currentFormat, $date);
		return $date->format($newFormat);
	}

	//Get availability_data
	public function GetAvailabilityData($MeetingsData)
	{
		// if $MeetingsData is object make it array
		$meeting_data =  !empty($MeetingsData) && is_array($MeetingsData) ? $MeetingsData : (array) $MeetingsData;
		$availability_data = isset($meeting_data['availability_custom']) ? $meeting_data['availability_custom'] : array();
		if (isset($meeting_data['availability_type']) && 'settings' === $meeting_data['availability_type']) {

			$host = new Host();
			$host = $host->getHostById($meeting_data['host_id']);


			$_tfhb_availability_settings = get_user_meta($host->user_id, '_tfhb_host', true);
			if (isset($_tfhb_availability_settings['availability_type']) && $_tfhb_availability_settings['availability_type'] == 'settings') {
				$host_settings_availability_id = $_tfhb_availability_settings['availability_id'];
				$_tfhb_availability_settings =  get_option('_tfhb_availability_settings');

				if (is_array($_tfhb_availability_settings)) {
					$key = array_search($host_settings_availability_id, array_column($_tfhb_availability_settings, 'id'));
					//  _tfhb_availability_settings index id wich is match with host settings availability id
					if (isset($_tfhb_availability_settings[$key])) {

						$availability_data = $_tfhb_availability_settings[$key];
					}
				}
			} elseif (isset($_tfhb_availability_settings['availability']) && in_array($meeting_data['availability_id'], array_keys($_tfhb_availability_settings['availability']))) {

				$availability_data = $_tfhb_availability_settings['availability'][$meeting_data['availability_id']];
			}
		}

		$availability_data = !is_array($availability_data) ? json_decode($availability_data, true) : $availability_data;

		return $availability_data;
	}
}
