<?php
/**
 * Analytics Helpers.
 *
 * @since 1.3.9
 * @package tripzzy
 */

namespace Tripzzy\Core\Helpers;

// Exit if accessed directly.
defined( 'ABSPATH' ) || exit;

use Tripzzy\Core\Bookings;
use Tripzzy\Core\Payment;
use Tripzzy\Core\Http\Nonce;

if ( ! class_exists( 'Tripzzy\Core\Helpers\Analytics' ) ) {
	/**
	 * Analytics Helper class.
	 *
	 * @since 1.3.9
	 */
	class Analytics {

		/**
		 * Get all stats.
		 *
		 * @since 1.3.9
		 */
		public function get_stats() {

			$start_date = '';
			$end_date   = '';
			$compare_to = '';

			if ( Nonce::verify() ) {
				$start_date = isset( $_GET['start_date'] ) ? sanitize_text_field( $_GET['start_date'] ) : ''; // @phpcs:ignore
				$end_date   = isset( $_GET['end_date'] ) ? sanitize_text_field( $_GET['end_date'] ) : ''; // @phpcs:ignore
				$compare_to = isset( $_GET['compare_to'] ) ? sanitize_text_field( $_GET['compare_to'] ) : ''; // @phpcs:ignore
			}

			$current_period_stats = $this->calculate_stats( $start_date, $end_date );

			// Calculate previous period stats only if a date range is set.
			$previous_period_stats = array(
				'gross_collection' => 0,
				'total_paid'       => 0,
				'total_unpaid'     => 0,
				'avg_order_value'  => 0,
				'total_bookings'   => 0,
			);
			if ( ! empty( $start_date ) && ! empty( $end_date ) ) {
				$previous_period_stats = $this->get_previous_period_stats( $start_date, $end_date, $compare_to );
			}

			$gross_collection = $current_period_stats['gross_collection'];
			$total_paid       = $current_period_stats['total_paid'];
			$total_unpaid     = $current_period_stats['total_unpaid'];
			$avg_order_value  = $current_period_stats['avg_order_value'];
			$total_bookings   = $current_period_stats['total_bookings'];
			$popular_trips    = $current_period_stats['popular_trips'];
			$chart_data       = $current_period_stats['chart_data'];

			return array(
				'gross_collection'                => Amount::display( $gross_collection ),
				'total_paid'                      => Amount::display( $total_paid ),
				'total_unpaid'                    => Amount::display( $total_unpaid ),
				'avg_order_value'                 => Amount::display( $avg_order_value ),
				'total_bookings'                  => number_format_i18n( $total_bookings ),
				'popular_trips'                   => $popular_trips,
				'chart_data'                      => $chart_data,
				'previous_period_gross'           => Amount::display( $previous_period_stats['gross_collection'] ),
				'previous_period_paid'            => Amount::display( $previous_period_stats['total_paid'] ),
				'previous_period_unpaid'          => Amount::display( $previous_period_stats['total_unpaid'] ),
				'previous_period_avg_order_value' => Amount::display( $previous_period_stats['avg_order_value'] ),
				'previous_period_total_bookings'  => number_format_i18n( $previous_period_stats['total_bookings'] ),
				'previous_period_start_date'      => $previous_period_stats['start_date'] ?? '',
				'previous_period_end_date'        => $previous_period_stats['end_date'] ?? '',
				'percent_change_gross'            => $this->calculate_percent_change( $gross_collection, $previous_period_stats['gross_collection'] ),
				'percent_change_paid'             => $this->calculate_percent_change( $total_paid, $previous_period_stats['total_paid'] ),
				'percent_change_unpaid'           => $this->calculate_percent_change( $total_unpaid, $previous_period_stats['total_unpaid'] ),
				'percent_change_avg_order'        => $this->calculate_percent_change( $avg_order_value, $previous_period_stats['avg_order_value'] ),
				'percent_change_bookings'         => $this->calculate_percent_change( $total_bookings, $previous_period_stats['total_bookings'] ),
			);
		}

		/**
		 * Calculate percentage change.
		 *
		 * @param float $current Current value.
		 * @param float $previous Previous value.
		 * @return array
		 */
		private function calculate_percent_change( $current, $previous ) {
			if ( 0 === (int) $previous ) {
				$percent = $current > 0 ? 100 : 0;
			} else {
				$percent = ( ( $current - $previous ) / $previous ) * 100;
			}
			return array(
				'value'     => abs( round( $percent, 2 ) ),
				'direction' => $percent >= 0 ? 'up' : 'down',
			);
		}

		/**
		 * Calculate stats for a given date range.
		 *
		 * @param string $start_date Start date.
		 * @param string $end_date End date.
		 * @since 1.3.9
		 * @return array
		 */
		private function calculate_stats( $start_date, $end_date ) {
			$args = array(
				'post_type'      => 'tripzzy_booking',
				'posts_per_page' => -1,
				'post_status'    => 'publish',
			);

			if ( ! empty( $start_date ) && ! empty( $end_date ) ) {
				$args['date_query'] = array(
					array(
						'after'     => $start_date,
						'before'    => $end_date,
						'inclusive' => true,
					),
				);
			}

			$bookings = get_posts( $args );

			$gross_collection = 0;
			$total_paid       = 0;
			$total_unpaid     = 0;
			$total_bookings   = count( $bookings );
			$trip_bookings    = array();
			$chart_data       = array(
				'gross_collection' => array(),
				'total_paid'       => array(),
				'total_unpaid'     => array(),
			);

			foreach ( $bookings as $booking ) {
				$booking_id   = $booking->ID;
				$booking_date = get_the_date( 'Y-m-d', $booking_id );
				$total        = Bookings::get_total( $booking_id );
				$paid         = Payment::get_total( $booking_id );
				$unpaid       = $total - $paid;

				$gross_collection += $total;
				$total_paid       += $paid;

				if ( ! isset( $chart_data['gross_collection'][ $booking_date ] ) ) {
					$chart_data['gross_collection'][ $booking_date ] = 0;
				}
				$chart_data['gross_collection'][ $booking_date ] += $total;

				if ( ! isset( $chart_data['total_paid'][ $booking_date ] ) ) {
					$chart_data['total_paid'][ $booking_date ] = 0;
				}
				$chart_data['total_paid'][ $booking_date ] += $paid;

				if ( ! isset( $chart_data['total_unpaid'][ $booking_date ] ) ) {
					$chart_data['total_unpaid'][ $booking_date ] = 0;
				}
				$chart_data['total_unpaid'][ $booking_date ] += $unpaid;

				$trip_ids = Bookings::get_trip_ids( $booking_id );
				foreach ( $trip_ids as $trip_id ) {
					if ( ! isset( $trip_bookings[ $trip_id ] ) ) {
						$trip_bookings[ $trip_id ] = 0;
					}
					++$trip_bookings[ $trip_id ];
				}
			}

			arsort( $trip_bookings );
			ksort( $chart_data['gross_collection'] );
			ksort( $chart_data['total_paid'] );
			ksort( $chart_data['total_unpaid'] );

			$total_unpaid    = $gross_collection - $total_paid;
			$avg_order_value = $total_bookings > 0 ? $gross_collection / $total_bookings : 0;

			return array(
				'gross_collection' => $gross_collection,
				'total_paid'       => $total_paid,
				'total_unpaid'     => $total_unpaid,
				'avg_order_value'  => $avg_order_value,
				'total_bookings'   => $total_bookings,
				'popular_trips'    => $trip_bookings,
				'chart_data'       => $chart_data,
			);
		}

		/**
		 * Get stats for the previous period.
		 *
		 * @param string $start_date Current period start date.
		 * @param string $end_date Current period end date.
		 * @param string $compare_to Comparison period type (day, week, month).
		 * @since 1.3.9
		 * @return array
		 */
		private function get_previous_period_stats( $start_date, $end_date, $compare_to ) {
			if ( empty( $start_date ) || empty( $end_date ) ) {
				return array(
					'gross_collection' => 0,
					'total_paid'       => 0,
					'total_unpaid'     => 0,
					'avg_order_value'  => 0,
					'total_bookings'   => 0,
				);
			}

			$start_datetime = new \DateTime( $start_date );
			$end_datetime   = new \DateTime( $end_date );
			$interval       = $start_datetime->diff( $end_datetime );
			$days           = $interval->days;

			$previous_start_datetime = clone $start_datetime;
			$previous_end_datetime   = clone $end_datetime;

			switch ( $compare_to ) {
				case 'previous_day':
					$previous_start_datetime->modify( '-1 day' );
					$previous_end_datetime->modify( '-1 day' );
					break;
				case 'previous_week':
					$previous_start_datetime->modify( '-1 week' );
					$previous_end_datetime->modify( '-1 week' );
					break;
				case 'previous_month':
					$previous_start_datetime->modify( '-1 month' );
					$previous_end_datetime = clone $start_datetime;
					$previous_end_datetime->modify( '-1 day' );

					break;
				default: // Same period length.
					$previous_start_datetime->modify( '-' . ( $days + 1 ) . ' days' );
					$previous_end_datetime->modify( '-' . ( $days + 1 ) . ' days' );
					break;
			}

			$stats               = $this->calculate_stats( $previous_start_datetime->format( 'Y-m-d' ), $previous_end_datetime->format( 'Y-m-d' ) );
			$stats['start_date'] = $previous_start_datetime->format( 'Y-m-d' );
			$stats['end_date']   = $previous_end_datetime->format( 'Y-m-d' );
			return $stats;
		}
	}
}
