<?php
/**
 * Frontend shortcodes and AJAX handlers.
 *
 * @package MerchantKits\AttendanceManager
 */

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

/**
 * Registers public shortcodes, assets, and clock/leave AJAX endpoints.
 */
class MKAM_Frontend {

	/**
	 * Register shortcodes, assets, and AJAX actions.
	 */
	public function __construct() {
		add_shortcode( 'mkam_clock_in', array( $this, 'shortcode_clock_in' ) );
		add_shortcode( 'mkam_my_attendance', array( $this, 'shortcode_my_attendance' ) );
		add_shortcode( 'mkam_leave_request', array( $this, 'shortcode_leave_request' ) );
		add_shortcode( 'mkam_my_leaves', array( $this, 'shortcode_my_leaves' ) );

		add_action( 'wp_enqueue_scripts', array( $this, 'register_assets' ) );

		add_action( 'wp_ajax_mkam_clock_in', array( $this, 'ajax_clock_in' ) );
		add_action( 'wp_ajax_mkam_clock_out', array( $this, 'ajax_clock_out' ) );
		add_action( 'wp_ajax_mkam_submit_leave', array( $this, 'ajax_submit_leave' ) );
		add_action( 'wp_ajax_mkam_cancel_leave', array( $this, 'ajax_cancel_leave' ) );
		add_action( 'wp_ajax_mkam_leave_balance', array( $this, 'ajax_leave_balance' ) );
	}

	/**
	 * Register (and enqueue) frontend CSS/JS.
	 *
	 * @return void
	 */
	public function register_assets() {
		wp_register_style(
			'mkam-frontend',
			MKAM_PLUGIN_URL . 'assets/css/frontend.css',
			array(),
			MKAM_VERSION
		);

		wp_register_script(
			'mkam-frontend',
			MKAM_PLUGIN_URL . 'assets/js/frontend.js',
			array(),
			MKAM_VERSION,
			true
		);

		wp_localize_script(
			'mkam-frontend',
			'mkamFrontend',
			array(
				'ajaxUrl'         => admin_url( 'admin-ajax.php' ),
				'nonce'           => wp_create_nonce( 'mkam_frontend' ),
				'enableGeo'       => (bool) get_option( 'mkam_enable_geolocation', 0 ),
				// UTC epoch (seconds) and site UTC offset (seconds) so the live clock
				// and worked-hours counter follow the site timezone, not the browser's.
				'serverNow'       => time(),
				'gmtOffset'       => (int) ( (float) get_option( 'gmt_offset', 0 ) * HOUR_IN_SECONDS ),
				'i18n'            => array(
					'clockInError'  => __( 'Could not clock in. Please try again.', 'merchantkits-attendance-manager' ),
					'clockOutError' => __( 'Could not clock out. Please try again.', 'merchantkits-attendance-manager' ),
				),
			)
		);
	}

	/**
	 * Resolve the employee linked to the current user, or null.
	 *
	 * @return array|null
	 */
	private function current_employee() {
		if ( ! is_user_logged_in() ) {
			return null;
		}

		return MKAM_Employee::get_by_user( get_current_user_id() );
	}

	/**
	 * Render a "please log in" notice.
	 *
	 * @return string
	 */
	private function login_notice() {
		return '<div class="mkam-widget mkam-notice">' . esc_html__( 'Please log in to access this feature.', 'merchantkits-attendance-manager' ) . '</div>';
	}

	/**
	 * Load a frontend template (theme override first) into a string.
	 *
	 * @param string $template Template filename.
	 * @param array  $context  Variables for the template.
	 * @return string
	 */
	private function view( $template, $context = array() ) {
		$theme_path  = trailingslashit( get_stylesheet_directory() ) . 'mkam/' . $template;
		$plugin_path = MKAM_TEMPLATES_DIR . $template;
		$file        = is_readable( $theme_path ) ? $theme_path : $plugin_path;

		if ( ! is_readable( $file ) ) {
			return '';
		}

		// phpcs:ignore WordPress.PHP.DontExtract.extract_extract
		extract( $context );

		ob_start();
		include $file;
		return ob_get_clean();
	}

	/* ---------------------------------------------------------------------
	 * Shortcodes
	 * ------------------------------------------------------------------- */

	/**
	 * [mkam_clock_in] clock in/out widget.
	 *
	 * @param array $atts Shortcode attributes.
	 * @return string
	 */
	public function shortcode_clock_in( $atts ) {
		$atts = apply_filters( 'mkam_shortcode_atts', (array) $atts, 'mkam_clock_in' );

		$employee = $this->current_employee();
		if ( ! $employee ) {
			return $this->login_notice();
		}

		wp_enqueue_style( 'mkam-frontend' );
		wp_enqueue_script( 'mkam-frontend' );

		$open = MKAM_Attendance::get_open( $employee['id'] );

		return $this->view(
			'clock-in-form.php',
			array(
				'employee' => $employee,
				'open'     => $open,
			)
		);
	}

	/**
	 * [mkam_my_attendance] calendar history widget.
	 *
	 * @param array $atts Shortcode attributes.
	 * @return string
	 */
	public function shortcode_my_attendance( $atts ) {
		$atts = apply_filters( 'mkam_shortcode_atts', (array) $atts, 'mkam_my_attendance' );

		$employee = $this->current_employee();
		if ( ! $employee ) {
			return $this->login_notice();
		}

		wp_enqueue_style( 'mkam-frontend' );
		wp_enqueue_script( 'mkam-frontend' );

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$month = isset( $_GET['mkam_month'] ) ? absint( $_GET['mkam_month'] ) : (int) current_time( 'n' );
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$year = isset( $_GET['mkam_year'] ) ? absint( $_GET['mkam_year'] ) : (int) current_time( 'Y' );

		$month = min( 12, max( 1, $month ) );

		$records = MKAM_Attendance::query(
			array(
				'employee_id' => $employee['id'],
				'date_from'   => sprintf( '%04d-%02d-01', $year, $month ),
				'date_to'     => gmdate( 'Y-m-t', strtotime( sprintf( '%04d-%02d-01', $year, $month ) ) ),
				'order'       => 'ASC',
			)
		);

		$by_date = array();
		foreach ( $records as $record ) {
			$by_date[ $record['date'] ] = $record;
		}

		// Overlay approved leave days so they show as "on leave" on the calendar.
		$month_start = sprintf( '%04d-%02d-01', $year, $month );
		$month_end   = gmdate( 'Y-m-t', strtotime( $month_start ) );
		$approved    = MKAM_Leave::query(
			array(
				'employee_id' => $employee['id'],
				'status'      => 'approved',
			)
		);

		foreach ( $approved as $leave ) {
			$day = max( strtotime( $leave['start_date'] ), strtotime( $month_start ) );
			$end = min( strtotime( $leave['end_date'] ), strtotime( $month_end ) );

			while ( $day <= $end ) {
				$date = gmdate( 'Y-m-d', $day );
				// Only fill days without an actual attendance record.
				if ( ! isset( $by_date[ $date ] ) ) {
					$by_date[ $date ] = array(
						'date'           => $date,
						'status'         => 'on_leave',
						'clock_in'       => null,
						'total_hours'    => 0,
						'overtime_hours' => 0,
					);
				}
				$day += DAY_IN_SECONDS;
			}
		}

		return $this->view(
			'my-attendance.php',
			array(
				'employee' => $employee,
				'month'    => $month,
				'year'     => $year,
				'by_date'  => $by_date,
			)
		);
	}

	/**
	 * [mkam_leave_request] leave request form.
	 *
	 * @param array $atts Shortcode attributes.
	 * @return string
	 */
	public function shortcode_leave_request( $atts ) {
		$atts = apply_filters( 'mkam_shortcode_atts', (array) $atts, 'mkam_leave_request' );

		$employee = $this->current_employee();
		if ( ! $employee ) {
			return $this->login_notice();
		}

		wp_enqueue_style( 'mkam-frontend' );
		wp_enqueue_script( 'mkam-frontend' );

		return $this->view(
			'leave-request-form.php',
			array(
				'employee' => $employee,
				'policies' => MKAM_Leave::get_policies(),
			)
		);
	}

	/**
	 * [mkam_my_leaves] leave dashboard.
	 *
	 * @param array $atts Shortcode attributes.
	 * @return string
	 */
	public function shortcode_my_leaves( $atts ) {
		$atts = apply_filters( 'mkam_shortcode_atts', (array) $atts, 'mkam_my_leaves' );

		$employee = $this->current_employee();
		if ( ! $employee ) {
			return $this->login_notice();
		}

		wp_enqueue_style( 'mkam-frontend' );
		wp_enqueue_script( 'mkam-frontend' );

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		$year = isset( $_GET['mkam_year'] ) ? absint( $_GET['mkam_year'] ) : (int) current_time( 'Y' );

		$leaves = MKAM_Leave::query(
			array(
				'employee_id' => $employee['id'],
				'year'        => $year,
			)
		);

		return $this->view(
			'my-leaves.php',
			array(
				'employee' => $employee,
				'policies' => MKAM_Leave::get_policies(),
				'leaves'   => $leaves,
				'year'     => $year,
			)
		);
	}

	/* ---------------------------------------------------------------------
	 * AJAX handlers
	 * ------------------------------------------------------------------- */

	/**
	 * Verify the frontend nonce and return the current employee or send error.
	 *
	 * @return array Employee row.
	 */
	private function ajax_guard() {
		check_ajax_referer( 'mkam_frontend', 'nonce' );

		$employee = $this->current_employee();

		if ( ! $employee || 'active' !== $employee['status'] ) {
			wp_send_json_error( array( 'message' => __( 'You are not authorized to perform this action.', 'merchantkits-attendance-manager' ) ), 403 );
		}

		return $employee;
	}

	/**
	 * Validate that the client IP is allowed when IP restriction is on.
	 *
	 * @return void
	 */
	private function enforce_ip_restriction() {
		if ( ! get_option( 'mkam_restrict_ip', 0 ) ) {
			return;
		}

		$allowed = array_filter( array_map( 'trim', explode( ',', (string) get_option( 'mkam_allowed_ips', '' ) ) ) );
		$ip      = MKAM_Attendance::get_client_ip();

		if ( ! empty( $allowed ) && ! in_array( $ip, $allowed, true ) ) {
			wp_send_json_error( array( 'message' => __( 'Clock-in is not allowed from your network.', 'merchantkits-attendance-manager' ) ), 403 );
		}
	}

	/**
	 * AJAX: clock in.
	 *
	 * @return void
	 */
	public function ajax_clock_in() {
		$employee = $this->ajax_guard();
		$this->enforce_ip_restriction();

		$today = current_time( 'Y-m-d' );

		if ( MKAM_Attendance::get_open( $employee['id'] ) ) {
			wp_send_json_error( array( 'message' => __( 'You are already clocked in.', 'merchantkits-attendance-manager' ) ) );
		}

		if ( MKAM_Attendance::get_for_date( $employee['id'], $today ) ) {
			wp_send_json_error( array( 'message' => __( 'Attendance already recorded for today.', 'merchantkits-attendance-manager' ) ) );
		}

		$now      = current_time( 'mysql' );
		$shift    = $employee['shift_id'] ? MKAM_Shift::get( $employee['shift_id'] ) : array();
		$late     = MKAM_Attendance::calculate_late_minutes( $now, (array) $shift );
		$note     = isset( $_POST['note'] ) ? sanitize_textarea_field( wp_unslash( $_POST['note'] ) ) : '';
		$location = isset( $_POST['location'] ) ? sanitize_text_field( wp_unslash( $_POST['location'] ) ) : '';

		// Allow Pro features (e.g. geofencing) to deny a clock-in. A non-empty
		// return value is treated as the denial message shown to the employee.
		$denied = apply_filters( 'mkam_clock_in_denied', '', $location, $employee );
		if ( ! empty( $denied ) ) {
			wp_send_json_error( array( 'message' => (string) $denied ), 403 );
		}

		$data = apply_filters(
			'mkam_clock_in_data',
			array(
				'employee_id'       => $employee['id'],
				'date'              => $today,
				'clock_in'          => $now,
				'clock_in_ip'       => MKAM_Attendance::get_client_ip(),
				'clock_in_location' => $location,
				'clock_in_note'     => $note,
				'status'            => $late > 0 ? 'late' : 'present',
				'late_minutes'      => $late,
			),
			$employee['id']
		);

		$id = MKAM_Attendance::create( $data );

		if ( is_wp_error( $id ) ) {
			wp_send_json_error( array( 'message' => $id->get_error_message() ) );
		}

		do_action( 'mkam_clock_in', $id, $employee['id'] );

		wp_send_json_success(
			array(
				'message'  => __( 'Clocked in successfully.', 'merchantkits-attendance-manager' ),
				'clock_in' => mysql2date( get_option( 'time_format', 'g:i a' ), $now ),
				'late'     => $late,
			)
		);
	}

	/**
	 * AJAX: clock out.
	 *
	 * @return void
	 */
	public function ajax_clock_out() {
		$employee = $this->ajax_guard();

		$open = MKAM_Attendance::get_open( $employee['id'] );

		if ( ! $open ) {
			wp_send_json_error( array( 'message' => __( 'You are not currently clocked in.', 'merchantkits-attendance-manager' ) ) );
		}

		$now      = current_time( 'mysql' );
		$shift    = $employee['shift_id'] ? MKAM_Shift::get( $employee['shift_id'] ) : array();
		$hours    = MKAM_Attendance::calculate_hours( $open['clock_in'], $now, (array) $shift );
		$note     = isset( $_POST['note'] ) ? sanitize_textarea_field( wp_unslash( $_POST['note'] ) ) : '';
		$location = isset( $_POST['location'] ) ? sanitize_text_field( wp_unslash( $_POST['location'] ) ) : '';

		$data = apply_filters(
			'mkam_clock_out_data',
			array(
				'clock_out'          => $now,
				'clock_out_ip'       => MKAM_Attendance::get_client_ip(),
				'clock_out_location' => $location,
				'clock_out_note'     => $note,
				'total_hours'        => $hours['total_hours'],
				'overtime_hours'     => $hours['overtime_hours'],
			),
			$open['id']
		);

		$result = MKAM_Attendance::update( $open['id'], $data );

		if ( is_wp_error( $result ) ) {
			wp_send_json_error( array( 'message' => $result->get_error_message() ) );
		}

		do_action( 'mkam_clock_out', (int) $open['id'], (int) $employee['id'] );

		wp_send_json_success(
			array(
				'message'     => __( 'Clocked out successfully.', 'merchantkits-attendance-manager' ),
				'clock_out'   => mysql2date( get_option( 'time_format', 'g:i a' ), $now ),
				'total_hours' => $hours['total_hours'],
			)
		);
	}

	/**
	 * AJAX: submit a leave request.
	 *
	 * @return void
	 */
	public function ajax_submit_leave() {
		$employee = $this->ajax_guard();

		$is_half_day = ! empty( $_POST['is_half_day'] );

		$data = apply_filters(
			'mkam_leave_request_data',
			array(
				'employee_id'     => $employee['id'],
				'leave_type'      => isset( $_POST['leave_type'] ) ? sanitize_text_field( wp_unslash( $_POST['leave_type'] ) ) : '',
				'start_date'      => isset( $_POST['start_date'] ) ? sanitize_text_field( wp_unslash( $_POST['start_date'] ) ) : '',
				'end_date'        => isset( $_POST['end_date'] ) ? sanitize_text_field( wp_unslash( $_POST['end_date'] ) ) : '',
				'is_half_day'     => $is_half_day ? 1 : 0,
				'half_day_period' => isset( $_POST['half_day_period'] ) ? sanitize_text_field( wp_unslash( $_POST['half_day_period'] ) ) : null,
				'reason'          => isset( $_POST['reason'] ) ? sanitize_textarea_field( wp_unslash( $_POST['reason'] ) ) : '',
			),
			$employee['id']
		);

		$id = MKAM_Leave::create( $data );

		if ( is_wp_error( $id ) ) {
			wp_send_json_error( array( 'message' => $id->get_error_message() ) );
		}

		wp_send_json_success( array( 'message' => __( 'Leave request submitted.', 'merchantkits-attendance-manager' ) ) );
	}

	/**
	 * AJAX: cancel a pending leave request (own only).
	 *
	 * @return void
	 */
	public function ajax_cancel_leave() {
		$employee = $this->ajax_guard();

		$leave_id = isset( $_POST['leave_id'] ) ? absint( $_POST['leave_id'] ) : 0;
		$leave    = MKAM_Leave::get( $leave_id );

		if ( ! $leave || (int) $leave['employee_id'] !== (int) $employee['id'] ) {
			wp_send_json_error( array( 'message' => __( 'Leave request not found.', 'merchantkits-attendance-manager' ) ), 404 );
		}

		if ( 'pending' !== $leave['status'] ) {
			wp_send_json_error( array( 'message' => __( 'Only pending requests can be cancelled.', 'merchantkits-attendance-manager' ) ) );
		}

		MKAM_Leave::update( $leave_id, array( 'status' => 'cancelled' ) );
		do_action( 'mkam_leave_cancelled', $leave_id, $employee['id'] );

		wp_send_json_success( array( 'message' => __( 'Leave request cancelled.', 'merchantkits-attendance-manager' ) ) );
	}

	/**
	 * AJAX: get the current employee's balance for a leave type.
	 *
	 * @return void
	 */
	public function ajax_leave_balance() {
		$employee = $this->ajax_guard();

		$leave_type = isset( $_POST['leave_type'] ) ? sanitize_text_field( wp_unslash( $_POST['leave_type'] ) ) : '';
		$balance    = MKAM_Leave::get_balance( $employee['id'], $leave_type );

		wp_send_json_success( $balance );
	}
}
