<?php

namespace Limb_Chatbot\Includes\Services\Chatbot_Analytics\Calculators;

use Limb_Chatbot\Includes\Data_Objects\Chatbot;
use Limb_Chatbot\Includes\Data_Objects\Lead;
use Limb_Chatbot\Includes\Services\Chatbot_Analytics\Data\Analytics_Data;
use Limb_Chatbot\Includes\Services\Chatbot_Analytics\Data\Analytics_Data_Point;
use Limb_Chatbot\Includes\Services\Chatbot_Analytics\Types\Analytics_Type;

/**
 * Class Leads_Captured_Calculator
 *
 * Calculates leads captured analytics by counting unique leads from the leads table
 * based on created_at timestamps within the given period. Supports flexible period bucketing
 * (hourly, daily, weekly, monthly, yearly).
 *
 * @package Limb_Chatbot\Includes\Services\Chatbot_Analytics\Calculators
 * @since 1.0.0
 */
class Leads_Captured_Calculator implements Analytics_Calculator {

	/**
	 * Calculate leads captured analytics for a chatbot.
	 *
	 * @param Analytics_Type $type          The analytics type (must be 'leads_captured')
	 * @param int|null       $starting_from Unix timestamp in UTC+0 or null for 'all'
	 * @param int            $ending_at    Unix timestamp in UTC+0 for the end of the period
	 * @param Chatbot        $chatbot       The chatbot to filter by
	 * @param float          $utc_offset    UTC offset in hours for timezone conversion (default: 0.0)
	 *
	 * @return Analytics_Data The calculated leads captured analytics
	 * @since 1.0.0
	 */
	public function calculate( Analytics_Type $type, ?int $starting_from, int $ending_at, Chatbot $chatbot, float $utc_offset = 0.0 ): Analytics_Data {

		// Determine bucketing strategy based on time range
		$strategy = Period_Bucketing::get_strategy( $starting_from, $ending_at );

		// Get buckets for this strategy (with UTC offset for timezone conversion)
		$buckets = Period_Bucketing::get_buckets( $strategy, $starting_from, $ending_at, $utc_offset );

		// Fetch leads captured records from database
		// Returns Lead objects for bucketing by created_at value
		$all_leads = $this->get_leads_captured_records( $chatbot, $starting_from, $ending_at );

		// Create data points by aggregating leads into buckets
		$data_points = [];
		foreach ( $buckets as $bucket ) {
			$count = $this->count_leads_in_bucket( $all_leads, $bucket['start'], $bucket['end'] );

			$data_points[] = new Analytics_Data_Point(
				$bucket['label'],
				$count
			);
		}

		// Current total is count of all leads in period (not grouped by user)
		$current_total = count( $all_leads );

		// Calculate comparison with previous period
		$comparison_result = $this->calculate_comparison(
			$starting_from,
			$ending_at,
			$current_total,
			$chatbot
		);

		// Determine period label based on starting_from
		$period_label = $this->determine_period_label( $starting_from, $ending_at );

		// Create analytics data object
		$analytics_data = new Analytics_Data(
			$type,
			$period_label,
			$starting_from,
			$data_points
		);

		// Set the period totals
		$analytics_data->set_current_period_total( $comparison_result['current_total'] );
		$analytics_data->set_previous_period_total( $comparison_result['previous_total'] );

		return $analytics_data;
	}

	/**
	 * Fetch all leads captured records for a chatbot within optional time range.
	 *
	 * Fetches Lead records from the leads table where:
	 * - chatbot_uuid IS NULL OR chatbot_uuid = given chatbot UUID (if not empty)
	 * - created_at timestamp is in the given period
	 *
	 * Returns all leads (not grouped by user) in the period.
	 *
	 * @param Chatbot  $chatbot        The chatbot
	 * @param int|null $starting_from  Unix timestamp or null for 'all'
	 * @param int      $ending_at      Unix timestamp
	 *
	 * @return array Array of Lead objects
	 * @since 1.0.0
	 */
	private function get_leads_captured_records( Chatbot $chatbot, ?int $starting_from, int $ending_at ): array {
		// Build WHERE conditions for date filtering
		$where = [];
		
		if ( null !== $starting_from ) {
			$start_datetime = gmdate( 'Y-m-d H:i:s', $starting_from );
			$end_datetime   = gmdate( 'Y-m-d H:i:s', $ending_at );
			$where['created_at>='] = $start_datetime;
			$where['created_at<='] = $end_datetime;
		}
		
		// Get all leads matching date range
		$all_leads = Lead::where( $where )->get();
		
		// Filter by chatbot_uuid: NULL OR matching chatbot UUID
		$filtered_leads = [];
		foreach ( $all_leads as $lead ) {
			// If chatbot has UUID, match NULL or the UUID
			if ( ! empty( $chatbot->uuid ) ) {
				if ( $lead->chatbot_uuid === null || $lead->chatbot_uuid === $chatbot->uuid ) {
					$filtered_leads[] = $lead;
				}
			} else {
				// If chatbot has no UUID, only match leads with NULL chatbot_uuid
				if ( $lead->chatbot_uuid === null ) {
					$filtered_leads[] = $lead;
				}
			}
		}
		
		return $filtered_leads;
	}

	/**
	 * Count all leads within a specific bucket (time range).
	 *
	 * @param array $leads   Array of Lead objects
	 * @param int   $bucket_start Unix timestamp for bucket start
	 * @param int   $bucket_end   Unix timestamp for bucket end
	 *
	 * @return int Count of leads in this bucket
	 * @since 1.0.0
	 */
	private function count_leads_in_bucket( array $leads, int $bucket_start, int $bucket_end ): int {
		$count = 0;

		foreach ( $leads as $lead ) {
			// Convert created_at timestamp to Unix timestamp
			// Format: '2026-01-16 13:55:37'
			$created_timestamp = strtotime( $lead->created_at );

			// Check if this lead was captured within the bucket time range
			if ( $created_timestamp >= $bucket_start && $created_timestamp < $bucket_end ) {
				$count++;
			}
		}

		return $count;
	}

	/**
	 * Calculate comparison with previous period.
	 *
	 * Compares current period leads captured count with previous same-length period.
	 * Returns an array with both period totals.
	 *
	 * @param int|null $starting_from Unix timestamp or null for 'all'
	 * @param int      $ending_at     Unix timestamp
	 * @param int      $current_count Current period unique leads count
	 * @param Chatbot  $chatbot       The chatbot
	 *
	 * @return array Array with keys: 'current_total', 'previous_total'
	 * @since 1.0.0
	 */
	private function calculate_comparison( ?int $starting_from, int $ending_at, $current_count, Chatbot $chatbot ): array {
		if ( null === $starting_from ) {
			// For 'all', no previous period to compare
			return [
				'current_total'  => $current_count,
				'previous_total' => 0,
			];
		}

		$period_length = $ending_at - $starting_from;
		$prev_start    = $starting_from - $period_length;
		$prev_end      = $starting_from;

		// Get Lead records in previous period using the same JOIN logic
		$prev_period_leads = $this->get_leads_captured_records( $chatbot, $prev_start, $prev_end );

		// Count all leads in previous period
		$previous_count = count( $prev_period_leads );

		return [
			'current_total'  => $current_count,
			'previous_total' => $previous_count,
		];
	}

	/**
	 * Determine human-readable period label.
	 *
	 * @param int|null $starting_from Unix timestamp or null for 'all'
	 * @param int      $ending_at     Unix timestamp
	 *
	 * @return string Period label
	 * @since 1.0.0
	 */
	private function determine_period_label( ?int $starting_from, int $ending_at ): string {
		if ( null === $starting_from ) {
			return __( 'All Time', 'limb-chatbot' );
		}

		$diff = $ending_at - $starting_from;
		$days = (int) ( $diff / ( 24 * 60 * 60 ) );

		if ( $days < 1 ) {
			$hours = (int) ( $diff / ( 60 * 60 ) );
			if ( $hours < 1 ) {
				$minutes = (int) ( $diff / 60 );
				return sprintf( _n( 'Last %d Minute', 'Last %d Minutes', $minutes, 'limb-chatbot' ), $minutes );
			}
			return sprintf( _n( 'Last %d Hour', 'Last %d Hours', $hours, 'limb-chatbot' ), $hours );
		}

		return sprintf( _n( 'Last %d Day', 'Last %d Days', $days, 'limb-chatbot' ), $days );
	}
}
