<?php

namespace Limb_Chatbot\Includes\Api\V1\Controllers;

use Limb_Chatbot\Includes\Factories\Chatbot_Analytics_Factory;
use Limb_Chatbot\Includes\Services\Chatbot_Analytics\Chatbot_Analytics_Service;
use Limb_Chatbot\Includes\Services\Chatbot_Analytics\Types\Analytics_Type_Registry;
use Limb_Chatbot\Includes\Data_Objects\Chatbot;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Exceptions\Exception;
use Limb_Chatbot\Includes\Services\Helper;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

class Chatbot_Analytics_Controller extends Rest_Controller {

	protected $rest_base = 'chatbots/(?P<chatbot_uuid>[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})/analytics';
	protected $default_rest_base = 'chatbots/default/analytics';

	public function __construct() {
		$this->service = new Chatbot_Analytics_Service();
	}

	public function register_routes(): void {
		register_rest_route( $this->namespace, '/' . $this->rest_base, array(
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => array( $this, 'get_items' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => array_merge( $this->get_chatbot_uuid(), $this->get_include_arg() ),
			)
		) );
		register_rest_route( $this->namespace, $this->default_rest_base, array(
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => array( $this, 'get_items' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => array_merge( $this->get_include_arg() ),
			)
		) );
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/types', array(
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => array( $this, 'get_types' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => array_merge( $this->get_chatbot_uuid() ),
			)
		) );
		register_rest_route( $this->namespace, $this->default_rest_base . '/types', array(
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => array( $this, 'get_types' ),
				'permission_callback' => array( $this, 'permission_callback' ),
				'args'                => array(),
			)
		) );
	}

	private function get_chatbot_uuid() {
		return array(
			'chatbot_uuid' => array(
				'description'       => __( 'Chatbot UUID', 'limb-chatbot' ),
				'type'              => 'string',
				'required'          => true,
				'sanitize_callback' => 'sanitize_key',
				'validate_callback' => function ( $value ) {
					return ! empty( Chatbot::exists_by_uuid( $value ) );
				}
			)
		);
	}

	private function get_include_arg() {
		return array(
			'include'       => array(
				'description'       => __( 'Array of analytics type IDs to include. Leave empty to include all types.',
					'limb-chatbot' ),
				'type'              => 'array',
				'sanitize_callback' => fn( $value ) => array_map( 'sanitize_text_field', (array) $value ),
			),
			'starting_from' => array(
				'description'       => __( 'Period filter. Use "2025-01-01 00:00:00" format for datetime (in UTC+0), "all" for no filter.',
					'limb-chatbot' ),
				'type'              => 'string',
				'default'           => 'all',
				'sanitize_callback' => 'sanitize_text_field',
			),
			'end_to'       => array(
				'description'       => __( 'Period end timestamp as Unix seconds (e.g., 1773604799). If omitted or empty, defaults to now.',
					'limb-chatbot' ),
				'type'              => 'integer',
				'sanitize_callback' => 'absint',
			),
			'utc'           => array(
				'description'       => __( 'UTC offset for timezone conversion (e.g., "+4", "-4", "+5.5"). Analytics will be returned in this timezone.',
					'limb-chatbot' ),
				'type'              => 'string',
				'default'           => '+0',
				'sanitize_callback' => 'sanitize_text_field',
			),
		);
	}

	/**
	 * Get analytics items for a chatbot.
	 *
	 * Supports the following query parameters:
	 * - include: Array of type IDs to include (e.g., ['conversations', 'leads_captured'])
	 * - starting_from: Period filter - 'all' (no filter), or datetime string (e.g., '2025-01-01 00:00:00')
	 * - end_to: Period end as Unix timestamp in seconds (e.g., 1773604799). If omitted/empty, defaults to now.
	 *
	 * @param  WP_REST_Request  $request  REST request object.
	 *
	 * @return WP_REST_Response|WP_Error
	 * @since 1.0.0
	 */
	public function get_items( $request ) {
		try {
			$chatbot_uuid = $request->get_param( 'chatbot_uuid' );
			$chatbot      = $chatbot_uuid ? Chatbot::find_by_uuid( $chatbot_uuid ) : Chatbot::make();

			if ( ! $chatbot instanceof Chatbot ) {
				throw new Exception( Error_Codes::NOT_FOUND, __( 'Chatbot not found', 'limb-chatbot' ) );
			}

			$include       = $request->get_param( 'include' );
			$starting_from = $request->get_param( 'starting_from' );
			$utc_offset    = $this->parse_utc_offset( $request->get_param( 'utc' ) );
			$ending_at     = $request->get_param( 'end_to' );

			// Default to 'all' if starting_from is not provided
			if ( empty( $starting_from ) ) {
				$starting_from = 'all';
			}

			$items = $this->service->get_analytics( $chatbot, is_array( $include ) ? $include : [], $starting_from, $utc_offset, $ending_at );

			// Convert Analytics_Data objects to arrays for JSON serialization
			$response_data = array_map( fn( $data ) => $data->to_array( $utc_offset ), $items ) ?? [];

			return rest_ensure_response( $response_data );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

			return Helper::get_wp_error( $e );
		}
	}

	/**
	 * Parse UTC offset string to hours (float).
	 *
	 * Supports formats like "+4", "-4", "+5.5", "-3.5"
	 *
	 * @param string|null $utc_offset UTC offset string
	 *
	 * @return float UTC offset in hours (default: 0.0)
	 * @since 1.0.13
	 */
	private function parse_utc_offset( ?string $utc_offset ): float {
		if ( empty( $utc_offset ) ) {
			return 0.0;
		}

		// Remove any whitespace
		$utc_offset = trim( $utc_offset );

		// Parse the offset (e.g., "+4", "-4", "+5.5")
		if ( preg_match( '/^([+-]?)(\d+(?:\.\d+)?)$/', $utc_offset, $matches ) ) {
			$sign   = $matches[1] === '-' ? -1 : 1;
			$hours  = (float) $matches[2];
			$offset = $sign * $hours;

			// Clamp to reasonable range (-12 to +14)
			return max( -12.0, min( 14.0, $offset ) );
		}

		return 0.0;
	}

	/**
	 * Get all available analytics types for a chatbot.
	 *
	 * Registers action types via factory, then returns all registered types.
	 * Supports both UUID and default chatbot.
	 *
	 * @param  WP_REST_Request  $request  REST request object.
	 *
	 * @return WP_REST_Response|WP_Error
	 * @since 1.0.0
	 */
	public function get_types( $request ) {
		try {
			$chatbot_uuid = $request->get_param( 'chatbot_uuid' );
			$chatbot      = $chatbot_uuid ? Chatbot::find_by_uuid( $chatbot_uuid ) : Chatbot::make();

			if ( ! $chatbot instanceof Chatbot ) {
				throw new Exception( Error_Codes::NOT_FOUND, __( 'Chatbot not found', 'limb-chatbot' ) );
			}

			// Register all action types for this chatbot via factory
			new Chatbot_Analytics_Factory( $chatbot );

			// Get all registered analytics types
			$registry  = Analytics_Type_Registry::instance();
			$all_types = $registry->get_all();

			// Convert types to array format for JSON response
			$types_data = [];
			foreach ( $all_types as $type ) {
				$types_data[] = [
					'id'         => $type->get_id(),
					'label'      => $type->get_label(),
					'name'       => $type->get_name(),
					'group'      => $type->get_group(),
					'is_builtin' => $type->is_builtin(),
				];
			}

			return rest_ensure_response( $types_data );
		} catch ( \Exception $e ) {
			Helper::log( $e, __METHOD__ );

			return Helper::get_wp_error( $e );
		}
	}

}