<?php
/**
 * Aggregator: turns raw events into daily rollups, then prunes old raw rows.
 *
 * Rollups exist purely for read performance — the dashboard never scans the raw
 * events table for historical ranges. Today's figures are always computed live
 * from raw; completed days are served from the rollup tables.
 *
 * @package Defog
 */

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

class Defog_Aggregator {

	public function __construct() {
		add_action( 'defog_rollup_event', array( $this, 'run' ) );
	}

	/**
	 * Roll up every completed day that isn't in the rollup tables yet, then prune.
	 */
	public function run() {
		global $wpdb;
		$events = defog_table( 'events' );
		$daily  = defog_table( 'stats_daily' );

		$today = defog_today();

		// Distinct event days strictly before today.
		$days = $wpdb->get_col(
			$wpdb->prepare(
				"SELECT DISTINCT DATE(occurred_at) AS d FROM %i WHERE occurred_at < %s ORDER BY d ASC",
					$events,
				$today . ' 00:00:00'
			)
		);

		foreach ( (array) $days as $day ) {
			// Skip days already rolled up.
			$exists = $wpdb->get_var( $wpdb->prepare( "SELECT 1 FROM %i WHERE stat_date = %s",
					$daily, $day ) );
			if ( $exists ) {
				continue;
			}
			$this->rollup_day( $day );
		}

		$this->prune();
	}

	/**
	 * Aggregate a single completed day.
	 *
	 * @param string $day Y-m-d.
	 */
	public function rollup_day( $day ) {
		global $wpdb;
		$events = defog_table( 'events' );
		$daily  = defog_table( 'stats_daily' );
		$dim    = defog_table( 'dim_daily' );

		$start = $day . ' 00:00:00';
		$end   = $day . ' 23:59:59';

		// KPI rollup.
		$pageviews = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM %i WHERE occurred_at BETWEEN %s AND %s",
					$events, $start, $end ) );
		$visitors  = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(DISTINCT visitor_hash) FROM %i WHERE occurred_at BETWEEN %s AND %s",
					$events, $start, $end ) );
		$sessions  = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COALESCE(SUM(is_new_session),0) FROM %i WHERE occurred_at BETWEEN %s AND %s",
					$events, $start, $end ) );

		// Bounces ≈ visitors with exactly one pageview that day.
		$bounces = (int) $wpdb->get_var(
			$wpdb->prepare(
				"SELECT COUNT(*) FROM (
					SELECT visitor_hash FROM %i
					WHERE occurred_at BETWEEN %s AND %s
					GROUP BY visitor_hash HAVING COUNT(*) = 1
				) t",
					$events,
				$start,
				$end
			)
		);

		$wpdb->replace(
			$daily,
			array(
				'stat_date' => $day,
				'visitors'  => $visitors,
				'pageviews' => $pageviews,
				'sessions'  => $sessions,
				'bounces'   => $bounces,
			),
			array( '%s', '%d', '%d', '%d', '%d' )
		);

		// Dimension rollups.
		$dimensions = array(
			'source'  => 'source',
			'country' => 'country',
			'device'  => 'device',
			'page'    => 'path',
		);

		foreach ( $dimensions as $dimension => $column ) {
			$rows = $wpdb->get_results(
				$wpdb->prepare(
					"SELECT %i AS label, COUNT(DISTINCT visitor_hash) AS visitors, COUNT(*) AS pageviews
					 FROM %i WHERE occurred_at BETWEEN %s AND %s
					 GROUP BY %i",
					$column,
					$events,
					$start,
					$end,
					$column
				)
			);

			foreach ( (array) $rows as $row ) {
				$label = substr( (string) $row->label, 0, 190 );
				$wpdb->replace(
					$dim,
					array(
						'stat_date'  => $day,
						'dimension'  => $dimension,
						'label'      => $label,
						'label_hash' => sha1( $dimension . '|' . $label ),
						'visitors'   => (int) $row->visitors,
						'pageviews'  => (int) $row->pageviews,
					),
					array( '%s', '%s', '%s', '%s', '%d', '%d' )
				);
			}
		}
	}

	/**
	 * Delete raw events older than the retention window (already rolled up).
	 */
	public function prune() {
		global $wpdb;
		$events    = defog_table( 'events' );
		$actions   = defog_table( 'actions' );
		$retention = defog_retention_days();
		$cutoff    = wp_date( 'Y-m-d', strtotime( '-' . $retention . ' days' ) ) . ' 00:00:00';

		$wpdb->query( $wpdb->prepare( "DELETE FROM %i WHERE occurred_at < %s",
					$events, $cutoff ) );
		$wpdb->query( $wpdb->prepare( "DELETE FROM %i WHERE occurred_at < %s",
					$actions, $cutoff ) );
	}
}
