<?php

namespace Limb_Chatbot\Includes\Services\Chatbot_Analytics\Data;

use Limb_Chatbot\Includes\Services\Chatbot_Analytics\Types\Analytics_Type;

/**
 * Class Analytics_Data
 *
 * Represents complete analytics data for a specific type.
 * This is the main data structure returned by analytics queries.
 * Implements the Value Object pattern to ensure data consistency.
 *
 * @package Limb_Chatbot\Includes\Services\Chatbot_Analytics\Data
 * @since 1.0.0
 */
class Analytics_Data {

	/**
	 * The analytics type.
	 *
	 * @var Analytics_Type
	 * @since 1.0.0
	 */
	private Analytics_Type $type;

	/**
	 * Human-readable label for this analytics data.
	 *
	 * @var string
	 * @since 1.0.0
	 */
	private string $label;

	/**
	 * Machine-readable name/ID for this analytics data.
	 *
	 * @var string
	 * @since 1.0.0
	 */
	private string $name;

	/**
	 * The time period label (e.g., 'Last 7 Days', 'Last Month').
	 *
	 * @var string
	 * @since 1.0.0
	 */
	private string $period;

	/**
	 * Array of data points representing the timeline.
	 *
	 * @var Analytics_Data_Point[]
	 * @since 1.0.0
	 */
	private array $analytics;

	/**
	 * Unix timestamp indicating when analytics data starts from.
	 *
	 * @var int
	 * @since 1.0.0
	 */
	private int $starting_from;

	/**
	 * Additional metadata that can be attached.
	 *
	 * @var array
	 * @since 1.0.0
	 */
	private array $metadata = [];

	/**
	 * Total count for the current period.
	 *
	 * @var int
	 * @since 1.0.0
	 */
	private int $current_period_total = 0;

	/**
	 * Total count for the previous period.
	 *
	 * @var int
	 * @since 1.0.0
	 */
	private int $previous_period_total = 0;

	/**
	 * Analytics_Data constructor.
	 *
	 * @param Analytics_Type           $type          The analytics type
	 * @param string                   $period        The period label
	 * @param int|null                 $starting_from Unix timestamp or null for 'all' (no filter)
	 * @param Analytics_Data_Point[] $analytics     Array of data points
	 *
	 * @since 1.0.0
	 */
	public function __construct(
		Analytics_Type $type,
		string $period,
		?int $starting_from = null,
		array $analytics = []
	) {
		$this->type         = $type;
		$this->label        = $type->get_label();
		$this->name         = $type->get_name();
		$this->period       = $period;
		$this->analytics    = $analytics;
		$this->starting_from = $starting_from ?? 0;
	}

	/**
	 * Get the analytics type.
	 *
	 * @return Analytics_Type
	 * @since 1.0.0
	 */
	public function get_type(): Analytics_Type {
		return $this->type;
	}

	/**
	 * Get the label.
	 *
	 * @return string
	 * @since 1.0.0
	 */
	public function get_label(): string {
		return $this->label;
	}

	/**
	 * Get the name/ID.
	 *
	 * @return string
	 * @since 1.0.0
	 */
	public function get_name(): string {
		return $this->name;
	}

	/**
	 * Get the period label.
	 *
	 * @return string
	 * @since 1.0.0
	 */
	public function get_period(): string {
		return $this->period;
	}

	/**
	 * Get all analytics data points.
	 *
	 * @return Analytics_Data_Point[]
	 * @since 1.0.0
	 */
	public function get_analytics(): array {
		return $this->analytics;
	}

	/**
	 * Add a data point to analytics.
	 *
	 * @param Analytics_Data_Point $point The data point to add
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function add_analytics_point( Analytics_Data_Point $point ): void {
		$this->analytics[] = $point;
	}

	/**
	 * Get the starting timestamp.
	 *
	 * @return int
	 * @since 1.0.0
	 */
	public function get_starting_from(): int {
		return $this->starting_from;
	}

	/**
	 * Set metadata value.
	 *
	 * @param string $key   The metadata key
	 * @param mixed  $value The metadata value
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function set_metadata( string $key, $value ): void {
		$this->metadata[ $key ] = $value;
	}

	/**
	 * Get metadata value.
	 *
	 * @param string $key     The metadata key
	 * @param mixed  $default Default value if key doesn't exist
	 *
	 * @return mixed
	 * @since 1.0.0
	 */
	public function get_metadata( string $key, $default = null ) {
		return $this->metadata[ $key ] ?? $default;
	}

	/**
	 * Get all metadata.
	 *
	 * @return array
	 * @since 1.0.0
	 */
	public function get_all_metadata(): array {
		return $this->metadata;
	}

	/**
	 * Set the current period total count.
	 *
	 * @param int $total Total count for current period
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function set_current_period_total( int $total ): void {
		$this->current_period_total = $total;
	}

	/**
	 * Get the current period total count.
	 *
	 * @return int
	 * @since 1.0.0
	 */
	public function get_current_period_total(): int {
		return $this->current_period_total;
	}

	/**
	 * Set the previous period total count.
	 *
	 * @param int $total Total count for previous period
	 *
	 * @return void
	 * @since 1.0.0
	 */
	public function set_previous_period_total( int $total ): void {
		$this->previous_period_total = $total;
	}

	/**
	 * Get the previous period total count.
	 *
	 * @return int
	 * @since 1.0.0
	 */
	public function get_previous_period_total(): int {
		return $this->previous_period_total;
	}

	/**
	 * Convert to array representation (for JSON serialization).
	 *
	 * @param float $utc_offset UTC offset in hours for timezone conversion (default: 0.0)
	 *
	 * @return array
	 * @since 1.0.0
	 */
	public function to_array( float $utc_offset = 0.0 ): array {
		// Convert starting_from from UTC+0 to user's timezone
		$starting_from_user = $this->starting_from;
		if ( $starting_from_user > 0 && $utc_offset != 0.0 ) {
			$starting_from_user = $starting_from_user + (int) ( $utc_offset * 3600 );
		}

		return [
			'label'                    => $this->label,
			'name'                     => $this->name,
			'type_id'                  => $this->type->get_id(),
			'type_group'               => $this->type->get_group(),
			'period'                   => $this->period,
			'current_period_total'     => $this->current_period_total,
			'previous_period_total'    => $this->previous_period_total,
			'starting_from'            => $starting_from_user,
			'analytics'                => array_map(
				fn( $point ) => $point->to_array(),
				$this->analytics
			),
			'metadata'                 => $this->metadata,
		];
	}

	/**
	 * Create from array (for deserialization).
	 *
	 * @param array          $data Array representation
	 * @param Analytics_Type $type The analytics type object
	 *
	 * @return self
	 * @since 1.0.0
	 */
	public static function from_array( array $data, Analytics_Type $type ): self {
		$analytics_points = array_map(
			fn( $point ) => Analytics_Data_Point::from_array( $point ),
			$data['analytics'] ?? []
		);

		$instance = new self(
			$type,
			$data['period'] ?? '',
			$data['starting_from'] ?? 0,
			$analytics_points
		);

		// Restore metadata
		foreach ( $data['metadata'] ?? [] as $key => $value ) {
			$instance->set_metadata( $key, $value );
		}

		return $instance;
	}
}
