<?php
/**
 * Billing-cycle window + usage totals — the single definition shared by the
 * Usage & Billing screen and the shareable usage report.
 *
 * The two surfaces used to derive the cycle differently (one anchored on the
 * subscription period start, the other on period end minus a month) and count
 * minutes differently (billing truth vs. a FLOOR of call duration), so the same
 * cycle could report different numbers. Everything now comes from here.
 *
 * @package    SiteStaffr
 * @subpackage SiteStaffr/includes
 */

if ( ! defined( 'WPINC' ) ) {
	die;
}

class SiteStaffr_Usage_Cycle {

	/**
	 * Base (current) cycle bounds, before any historical offset is applied.
	 *
	 * @return array{start_ts:int,end_ts:int}
	 */
	public static function get_base_window() {
		$billing_cache = array();
		if ( class_exists( 'SiteStaffr_Billing_State' ) ) {
			$cache = SiteStaffr_Billing_State::get_cache();
			if ( is_array( $cache ) ) {
				$billing_cache = $cache;
			}
		}
		$balance_option = get_option( 'sitestaffr_minute_balance', array() );
		$status_key     = isset( $billing_cache['subscription_status'] ) ? sanitize_key( $billing_cache['subscription_status'] ) : 'active';

		// Cycle start — subscription period start wins; the rest are fallbacks.
		$start_raw = '';
		if ( ! empty( $billing_cache['subscription_current_period_start'] ) ) {
			$start_raw = $billing_cache['subscription_current_period_start'];
		} elseif ( ! empty( $billing_cache['trial_started_at'] ) ) {
			$start_raw = $billing_cache['trial_started_at'];
		} elseif ( ! empty( $billing_cache['subscription_current_period_end'] ) && strtotime( $billing_cache['subscription_current_period_end'] ) ) {
			$start_raw = gmdate( 'Y-m-d H:i:s', strtotime( '-1 month', strtotime( $billing_cache['subscription_current_period_end'] ) ) );
		} elseif ( isset( $balance_option['last_reset_date'] ) ) {
			$start_raw = $balance_option['last_reset_date'] . ' 00:00:00';
		} else {
			$start_raw = current_time( 'Y-m-01 00:00:00' );
		}

		$start_ts = strtotime( $start_raw );
		if ( false === $start_ts ) {
			$start_ts = strtotime( current_time( 'Y-m-01 00:00:00' ) );
		}

		// Cycle end.
		$end_raw = '';
		if ( ! empty( $billing_cache['subscription_current_period_end'] ) ) {
			$end_raw = $billing_cache['subscription_current_period_end'];
		} elseif ( 'trial_active' === $status_key && ! empty( $billing_cache['trial_expires_at'] ) ) {
			$end_raw = $billing_cache['trial_expires_at'];
		}
		$end_ts = ! empty( $end_raw ) ? strtotime( $end_raw ) : false;
		if ( false === $end_ts || $end_ts < $start_ts ) {
			$end_ts = strtotime( current_time( 'mysql' ) );
		}

		return array(
			'start_ts' => (int) $start_ts,
			'end_ts'   => (int) $end_ts,
		);
	}

	/**
	 * Resolve a cycle window by historical offset (0 = current cycle).
	 *
	 * @param int $offset Cycles back from the current one.
	 * @return array Window: start/end timestamps, MySQL datetimes (query_end_dt is
	 *               clamped to now for the current cycle) and a display label.
	 */
	public static function get_window( $offset = 0 ) {
		$offset = max( 0, (int) $offset );
		$base   = self::get_base_window();

		$start_ts = strtotime( '-' . $offset . ' month', $base['start_ts'] );
		if ( false === $start_ts ) {
			$start_ts = $base['start_ts'];
		}

		$end_ts = strtotime( '-' . $offset . ' month', $base['end_ts'] );
		if ( false === $end_ts || $end_ts < $start_ts ) {
			$end_ts = $start_ts;
		}

		// The current cycle is only complete up to now; past cycles are whole.
		$now_ts = strtotime( current_time( 'mysql' ) );
		if ( false === $now_ts ) {
			$now_ts = time();
		}
		$query_end_ts = ( 0 === $offset ) ? min( $end_ts, $now_ts ) : $end_ts;
		if ( $query_end_ts < $start_ts ) {
			$query_end_ts = $start_ts;
		}

		return array(
			'offset'       => $offset,
			'start_ts'     => (int) $start_ts,
			'end_ts'       => (int) $end_ts,
			'start_dt'     => gmdate( 'Y-m-d H:i:s', $start_ts ),
			'end_dt'       => gmdate( 'Y-m-d H:i:s', $end_ts ),
			'query_end_dt' => gmdate( 'Y-m-d H:i:s', $query_end_ts ),
			'label'        => gmdate( 'M j', $start_ts ) . ' – ' . gmdate( 'M j, Y', $end_ts ),
		);
	}

	/**
	 * Usage totals for a cycle — the numbers every surface must agree on.
	 *
	 * Minutes are billing truth (what was deducted), so the report, the chart and
	 * the Usage screen cannot drift apart.
	 *
	 * @param int $offset Cycles back from the current one.
	 * @return array Totals plus the resolved window and per-day rows.
	 */
	public static function get_stats( $offset = 0 ) {
		require_once SITESTAFFR_PLUGIN_DIR . 'admin/class-sitestaffr-admin-view-query-service.php';

		$window   = self::get_window( $offset );
		$start_dt = $window['start_dt'];
		$end_dt   = $window['query_end_dt'];

		$query_service = new SiteStaffr_Admin_View_Query_Service();
		$counts        = $query_service->get_dashboard_period_counts( $start_dt, $end_dt );

		$usage_manager = SiteStaffr_Usage_Manager::get_instance();
		$usage_stats   = $usage_manager->get_usage_stats( $start_dt, $end_dt );
		$included_used = isset( $usage_stats['included_used'] ) ? (int) $usage_stats['included_used'] : 0;
		$purchased_used = isset( $usage_stats['purchased_used'] ) ? (int) $usage_stats['purchased_used'] : 0;

		$conversations = isset( $counts['total_calls'] ) ? (int) $counts['total_calls'] : 0;
		$billable      = isset( $counts['billable_calls'] ) ? (int) $counts['billable_calls'] : 0;

		$hours_state    = SiteStaffr_Business_Hours::get_state();
		$hours_schedule = SiteStaffr_Business_Hours::get_schedule();
		$after_hours    = $query_service->get_after_hours_stats(
			$start_dt,
			$end_dt,
			is_array( $hours_schedule ) ? $hours_schedule : array()
		);

		return array(
			'window'        => $window,
			'conversations' => $conversations,
			'billable'      => $billable,
			'filtered'      => max( 0, $conversations - $billable ),
			'leads'         => SiteStaffr_Leads::get_lead_count_between( $start_dt, $end_dt ),
			'included_used' => $included_used,
			'purchased_used' => $purchased_used,
			'minutes_used'  => $included_used + $purchased_used,
			'avg_seconds'   => (float) $query_service->get_average_call_duration_seconds_for_period( $start_dt, $end_dt ),
			'daily_minutes' => $query_service->get_daily_minutes_rows( $start_dt, $end_dt ),
			'daily_volume'  => $query_service->get_daily_conversation_rows( $start_dt, $end_dt ),
			'hours_state'   => $hours_state,
			'after_hours'   => $after_hours,
			'top_pages'     => $query_service->get_top_source_pages( $start_dt, $end_dt, 8 ),
		);
	}
}
