<?php
/**
 * Abilities API integration (WordPress 6.9+).
 *
 * Registers the read-only Kantan Analytics surface as abilities so AI agents
 * can query analytics data through the core Abilities API — and, when the
 * WordPress MCP Adapter plugin is active, through an MCP server at
 * /wp-json/kantan/mcp.
 *
 * Design notes:
 * - Every ability wraps an existing REST callback from rest-api.php via a
 *   synthetic WP_REST_Request, so query logic lives in exactly one place.
 * - Registration is config-driven: kantan_abilities_get_definitions() is the
 *   single map of what agents can see. Add an entry there, nothing else.
 * - Only read-only endpoints are exposed. Anything write-shaped, settings-
 *   shaped, or admin-diagnostic (excluded IPs, retention, bot filter,
 *   conversion links, agent generation) is deliberately NOT an ability: an
 *   autonomous agent must not be able to mutate state through this surface.
 * - The MCP server's tool list is discovered dynamically from the abilities
 *   registry (kantan-analytics/* and kantan-seo/*), so Kantan SEO abilities
 *   automatically join this server when that plugin is active.
 * - Registration is a no-op on WordPress < 6.9: the wp_abilities_api_* hooks
 *   simply never fire, and every callback re-guards with function_exists.
 *
 * @package Kantan_Analytics
 */

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

/**
 * Register the shared "analytics" ability category.
 *
 * Categories are global across plugins, so only register if no other plugin
 * has claimed the slug first (abilities may reference an existing category).
 */
function kantan_abilities_register_category() {
	if ( ! function_exists( 'wp_register_ability_category' ) || wp_has_ability_category( 'analytics' ) ) {
		return;
	}

	wp_register_ability_category(
		'analytics',
		array(
			'label'       => __( 'Analytics', 'kantan-analytics' ),
			'description' => __( 'Website traffic, engagement, and conversion data.', 'kantan-analytics' ),
		)
	);
}
add_action( 'wp_abilities_api_categories_init', 'kantan_abilities_register_category' );

/**
 * Permission check shared by all Kantan abilities.
 *
 * Mirrors kantan_rest_permission(): admins always; the read-only dashboard
 * viewer capability is sufficient because every registered ability is a GET
 * equivalent — nothing exposed here mutates state.
 *
 * @param mixed $input Ability input (unused).
 * @return bool Whether the current user may execute Kantan abilities.
 */
function kantan_ability_permission( $input = null ) {
	return current_user_can( 'manage_options' ) || current_user_can( 'kantan_view_dashboard' );
}

/**
 * Bridge an ability invocation onto an existing REST callback.
 *
 * Builds a synthetic WP_REST_Request from the (already schema-validated)
 * ability input, applying defaults and per-parameter sanitizers, then
 * unwraps the WP_REST_Response to the raw data array.
 *
 * @param callable $callback REST callback from rest-api.php.
 * @param mixed    $input    Ability input (object schema => array).
 * @param array    $params   Parameter config: key => array{default?: mixed, sanitize?: callable}.
 *                           Integer params may omit 'sanitize' (absint applied).
 * @return array|WP_Error Raw response data, or WP_Error from the callback.
 */
function kantan_ability_run_rest( $callback, $input, array $params ) {
	$request = new WP_REST_Request( 'GET' );

	foreach ( $params as $key => $config ) {
		$provided = is_array( $input ) && array_key_exists( $key, $input );

		if ( $provided ) {
			$value = $input[ $key ];
		} elseif ( array_key_exists( 'default', $config ) ) {
			$value = $config['default'];
		} else {
			continue; // Required-but-missing is already rejected by the input schema.
		}

		$sanitize = isset( $config['sanitize'] ) ? $config['sanitize'] : 'absint';
		$request->set_param( $key, call_user_func( $sanitize, $value ) );
	}

	$response = call_user_func( $callback, $request );
	if ( is_wp_error( $response ) ) {
		return $response;
	}

	return rest_ensure_response( $response )->get_data();
}

/**
 * Sanitize a visitor ID to the character set the REST route accepts.
 *
 * @param string $value Raw visitor ID.
 * @return string Sanitized visitor ID.
 */
function kantan_ability_sanitize_visitor_id( $value ) {
	return preg_replace( '/[^a-zA-Z0-9_]/', '', (string) $value );
}

/**
 * Input-schema fragment for the `days` parameter.
 *
 * @param int $default_days Default reporting window.
 * @return array<string, mixed>
 */
function kantan_ability_days_property( $default_days = 30 ) {
	return array(
		'type'        => 'integer',
		'minimum'     => 1,
		'maximum'     => 365,
		'default'     => $default_days,
		'description' => __( 'Reporting window in days, counted back from now.', 'kantan-analytics' ),
	);
}

/**
 * Input-schema fragment for a `limit` parameter.
 *
 * @param int    $default_limit Default row cap.
 * @param int    $max_limit     Maximum row cap.
 * @param string $description   What the limit bounds.
 * @return array<string, mixed>
 */
function kantan_ability_limit_property( $default_limit, $max_limit, $description ) {
	return array(
		'type'        => 'integer',
		'minimum'     => 1,
		'maximum'     => $max_limit,
		'default'     => $default_limit,
		'description' => $description,
	);
}

/**
 * Build an object input schema from property fragments.
 *
 * @param array $properties Property name => schema fragment.
 * @param array $required   Optional. Required property names.
 * @return array<string, mixed>
 */
function kantan_ability_input_schema( array $properties = array(), array $required = array() ) {
	$schema = array(
		'type'                 => 'object',
		'properties'           => $properties ? $properties : new stdClass(), // {} not [] when serialized for MCP clients.
		'additionalProperties' => false,
		'default'              => array(),
	);
	if ( $required ) {
		$schema['required'] = $required;
	}

	return $schema;
}

/**
 * Output-schema fragment for a list of labeled-count rows out of $wpdb.
 *
 * Note: $wpdb returns counts as numeric strings ("10"); the REST schema
 * validator accepts those for `integer`, so typing them as integer is safe
 * and tells agents what the value means.
 *
 * @param array<string, string|array> $props Property name => JSON Schema type.
 * @return array<string, mixed>
 */
function kantan_ability_row_schema( array $props ) {
	$properties = array();
	foreach ( $props as $name => $type ) {
		$properties[ $name ] = array( 'type' => $type );
	}

	return array(
		'type'  => 'array',
		'items' => array(
			'type'       => 'object',
			'properties' => $properties,
		),
	);
}

/**
 * The single source of truth for the agent-visible Kantan Analytics surface.
 *
 * Each entry becomes one ability named kantan-analytics/{key} and, via the
 * MCP adapter, one MCP tool. Descriptions are written for agents: they are
 * the "trigger" that makes a model pick the right tool, so every one ends
 * with a usage hint.
 *
 * @return array<string, array<string, mixed>>
 */
function kantan_abilities_get_definitions() {
	$deep_rows_note = __( 'Row objects may contain additional fields beyond those documented.', 'kantan-analytics' );

	return array(

		'get-analytics-overview' => array(
			'label'         => __( 'Get Analytics Overview', 'kantan-analytics' ),
			'description'   => __( 'Returns site-wide analytics for a period: total visits, unique visitors, average duration, bounce rate, traffic sources, daily visit series, top referrers, device and visitor-type breakdowns. Use this first to understand overall site performance.', 'kantan-analytics' ),
			'callback'      => 'kantan_rest_analytics',
			'params'        => array(
				'days' => array( 'default' => 30 ),
			),
			'input_schema'  => kantan_ability_input_schema(
				array( 'days' => kantan_ability_days_property() )
			),
			'output_schema' => array(
				'type'       => 'object',
				'properties' => array(
					'period_days'     => array( 'type' => 'integer' ),
					'total_visits'    => array( 'type' => 'integer' ),
					'unique_visitors' => array( 'type' => 'integer' ),
					'avg_duration'    => array(
						'type'        => 'integer',
						'description' => __( 'Average visit duration in seconds.', 'kantan-analytics' ),
					),
					'bounce_rate'     => array(
						'type'        => 'number',
						'description' => __( 'Bounce rate as a percentage (0-100).', 'kantan-analytics' ),
					),
					'traffic_sources' => kantan_ability_row_schema(
						array(
							'traffic_source' => 'string',
							'visits'         => 'integer',
						)
					),
					'ai_assistants'   => array( 'type' => 'array' ),
					'visits_by_day'   => kantan_ability_row_schema(
						array(
							'date'   => 'string',
							'visits' => 'integer',
						)
					),
					'top_referrers'   => kantan_ability_row_schema(
						array(
							'referrer' => 'string',
							'visits'   => 'integer',
						)
					),
					'devices'         => kantan_ability_row_schema(
						array(
							'device_type'  => 'string',
							'sessions'     => 'integer',
							'avg_duration' => 'number',
							'bounce_rate'  => 'number',
						)
					),
					'visitor_types'   => kantan_ability_row_schema(
						array(
							'visitor_type' => 'string',
							'sessions'     => 'integer',
							'avg_duration' => 'number',
							'bounce_rate'  => 'number',
						)
					),
				),
			),
		),

		'get-top-pages' => array(
			'label'         => __( 'Get Top Pages', 'kantan-analytics' ),
			'description'   => __( 'Returns the most viewed pages plus entry pages (where sessions start) and exit pages (where sessions end) for a period. Use to find which content attracts and loses visitors.', 'kantan-analytics' ),
			'callback'      => 'kantan_rest_pages',
			'params'        => array(
				'days'  => array( 'default' => 30 ),
				'limit' => array( 'default' => 20 ),
			),
			'input_schema'  => kantan_ability_input_schema(
				array(
					'days'  => kantan_ability_days_property(),
					'limit' => kantan_ability_limit_property( 20, 100, __( 'Maximum rows per list.', 'kantan-analytics' ) ),
				)
			),
			'output_schema' => array(
				'type'       => 'object',
				'properties' => array(
					'period_days' => array( 'type' => 'integer' ),
					'top_pages'   => kantan_ability_row_schema(
						array(
							'page_url'   => 'string',
							'page_title' => 'string',
							'views'      => 'integer',
						)
					),
					'entry_pages' => kantan_ability_row_schema(
						array(
							'page_url'   => 'string',
							'page_title' => 'string',
							'entries'    => 'integer',
						)
					),
					'exit_pages'  => kantan_ability_row_schema(
						array(
							'page_url'   => 'string',
							'page_title' => 'string',
							'exits'      => 'integer',
						)
					),
				),
			),
		),

		'get-traffic-sources' => array(
			'label'         => __( 'Get Traffic Sources', 'kantan-analytics' ),
			'description'   => __( 'Returns the traffic source breakdown (organic, direct, social, referral, AI assistants) and the top 20 referrers for a period. Use to understand where visitors come from.', 'kantan-analytics' ),
			'callback'      => 'kantan_rest_traffic',
			'params'        => array(
				'days' => array( 'default' => 30 ),
			),
			'input_schema'  => kantan_ability_input_schema(
				array( 'days' => kantan_ability_days_property() )
			),
			'output_schema' => array(
				'type'       => 'object',
				'properties' => array(
					'period_days' => array( 'type' => 'integer' ),
					'sources'     => kantan_ability_row_schema(
						array(
							'traffic_source' => 'string',
							'visits'         => 'integer',
						)
					),
					'referrers'   => kantan_ability_row_schema(
						array(
							'referrer' => 'string',
							'visits'   => 'integer',
						)
					),
				),
			),
		),

		'get-conversions' => array(
			'label'         => __( 'Get Conversions', 'kantan-analytics' ),
			'description'   => __( 'Returns conversion data for a period: totals by conversion type (phone, email, directions, forms, booking), breakdown by traffic source, and recent individual conversions. Use to see what visitors actually do.', 'kantan-analytics' ),
			'callback'      => 'kantan_rest_conversions',
			'params'        => array(
				'days'  => array( 'default' => 30 ),
				'limit' => array( 'default' => 50 ),
			),
			'input_schema'  => kantan_ability_input_schema(
				array(
					'days'  => kantan_ability_days_property(),
					'limit' => kantan_ability_limit_property( 50, 500, __( 'Maximum recent conversions to return.', 'kantan-analytics' ) ),
				)
			),
			'output_schema' => array(
				'type'       => 'object',
				'properties' => array(
					'period_days' => array( 'type' => 'integer' ),
					'summary'     => kantan_ability_row_schema(
						array(
							'conversion_type' => 'string',
							'total'           => 'integer',
						)
					),
					'by_source'   => array( 'type' => 'array' ),
					'recent'      => array(
						'type'  => 'array',
						'items' => array(
							'type'       => 'object',
							'properties' => array(
								'id'               => array( 'type' => 'integer' ),
								'session_id'       => array( 'type' => 'string' ),
								'visitor_id'       => array( 'type' => array( 'string', 'null' ) ),
								'conversion_type'  => array( 'type' => 'string' ),
								'conversion_label' => array( 'type' => array( 'string', 'null' ) ),
								'conversion_value' => array( 'type' => array( 'string', 'null' ) ),
								'page_url'         => array( 'type' => 'string' ),
								'converted_at'     => array( 'type' => 'string' ),
								'traffic_source'   => array( 'type' => array( 'string', 'null' ) ),
								'referrer'         => array( 'type' => array( 'string', 'null' ) ),
							),
						),
					),
				),
			),
		),

		'get-geo' => array(
			'label'         => __( 'Get Country Breakdown', 'kantan-analytics' ),
			'description'   => __( 'Returns visitor sessions per country (ISO country codes), bot-filtered, for a period. Use to see where in the world visitors are located.', 'kantan-analytics' ),
			'callback'      => 'kantan_rest_geo',
			'params'        => array(
				'days' => array( 'default' => 30 ),
			),
			'input_schema'  => kantan_ability_input_schema(
				array( 'days' => kantan_ability_days_property() )
			),
			'output_schema' => array(
				'type'       => 'object',
				'properties' => array(
					'period_days' => array( 'type' => 'integer' ),
					'countries'   => array( 'type' => 'array' ),
				),
			),
		),

		'get-referrers' => array(
			'label'         => __( 'Get All Referrers', 'kantan-analytics' ),
			'description'   => __( 'Returns the full referrer domain list with visit counts for a period (the overview only shows the top 10). Use when you need every referring site, or to find a domain for get-referrer-detail.', 'kantan-analytics' ),
			'callback'      => 'kantan_rest_referrers',
			'params'        => array(
				'days'  => array( 'default' => 30 ),
				'limit' => array( 'default' => 100 ),
			),
			'input_schema'  => kantan_ability_input_schema(
				array(
					'days'  => kantan_ability_days_property(),
					'limit' => kantan_ability_limit_property( 100, 1000, __( 'Maximum referrer domains to return.', 'kantan-analytics' ) ),
				)
			),
			'output_schema' => array(
				'type'       => 'object',
				'properties' => array(
					'period_days' => array( 'type' => 'integer' ),
					'total'       => array(
						'type'        => 'integer',
						'description' => __( 'Total distinct referrer domains in the period.', 'kantan-analytics' ),
					),
					'referrers'   => kantan_ability_row_schema(
						array(
							'domain' => 'string',
							'visits' => 'integer',
						)
					),
				),
			),
		),

		'get-referrer-detail' => array(
			'label'         => __( 'Get Referrer Detail', 'kantan-analytics' ),
			'description'   => __( 'Returns everything known about traffic from one referrer domain: session/visitor/pageview summary, daily series, exact referring URLs, pages visited, and the individual visitors it sent. Use after get-referrers to drill into a specific source.', 'kantan-analytics' ),
			'callback'      => 'kantan_rest_referrer_detail',
			'params'        => array(
				'domain' => array( 'sanitize' => 'sanitize_text_field' ),
				'days'   => array( 'default' => 30 ),
			),
			'input_schema'  => kantan_ability_input_schema(
				array(
					'domain' => array(
						'type'        => 'string',
						'description' => __( 'Referrer domain exactly as returned by get-referrers (e.g. "google.com", or "Direct / No Referrer").', 'kantan-analytics' ),
					),
					'days'   => kantan_ability_days_property(),
				),
				array( 'domain' )
			),
			'output_schema' => array(
				'type'        => 'object',
				'description' => $deep_rows_note,
				'properties'  => array(
					'domain'        => array( 'type' => 'string' ),
					'is_direct'     => array( 'type' => 'boolean' ),
					'total_visits'  => array( 'type' => 'integer' ),
					'summary'       => array( 'type' => 'object' ),
					'by_day'        => array( 'type' => 'array' ),
					'referrer_urls' => array( 'type' => 'array' ),
					'pages_visited' => array( 'type' => 'array' ),
					'visitors'      => array( 'type' => 'array' ),
				),
			),
		),

		'get-utm-campaigns' => array(
			'label'         => __( 'Get UTM Campaigns', 'kantan-analytics' ),
			'description'   => __( 'Returns UTM campaign performance (source/medium/campaign groupings with visits and conversions) plus recent UTM-tagged visits for a period. Use to evaluate marketing campaign traffic.', 'kantan-analytics' ),
			'callback'      => 'kantan_rest_utm',
			'params'        => array(
				'days'  => array( 'default' => 30 ),
				'limit' => array( 'default' => 50 ),
			),
			'input_schema'  => kantan_ability_input_schema(
				array(
					'days'  => kantan_ability_days_property(),
					'limit' => kantan_ability_limit_property( 50, 500, __( 'Maximum rows to return.', 'kantan-analytics' ) ),
				)
			),
			'output_schema' => array(
				'type'       => 'object',
				'properties' => array(
					'period_days' => array( 'type' => 'integer' ),
					'campaigns'   => array( 'type' => 'array' ),
					'recent'      => array( 'type' => 'array' ),
				),
			),
		),

		'get-user-journeys' => array(
			'label'         => __( 'Get User Journeys', 'kantan-analytics' ),
			'description'   => __( 'Returns recent session journeys (the ordered pages each visitor viewed, with durations and traffic source) and the most common page-to-page transitions for a period. Use to understand how visitors move through the site.', 'kantan-analytics' ),
			'callback'      => 'kantan_rest_journeys',
			'params'        => array(
				'days'  => array( 'default' => 30 ),
				'limit' => array( 'default' => 20 ),
			),
			'input_schema'  => kantan_ability_input_schema(
				array(
					'days'  => kantan_ability_days_property(),
					'limit' => kantan_ability_limit_property( 20, 100, __( 'Maximum journeys to return.', 'kantan-analytics' ) ),
				)
			),
			'output_schema' => array(
				'type'        => 'object',
				'description' => $deep_rows_note,
				'properties'  => array(
					'period_days' => array( 'type' => 'integer' ),
					'journeys'    => array( 'type' => 'array' ),
					'transitions' => kantan_ability_row_schema(
						array(
							'from_page'   => 'string',
							'to_page'     => 'string',
							'transitions' => 'integer',
						)
					),
				),
			),
		),

		'get-visitors' => array(
			'label'         => __( 'Get Visitor Profiles', 'kantan-analytics' ),
			'description'   => __( 'Returns visitor profiles for a period: visitor ID, generated display name, session count, first/last seen, and traffic sources, with per-session history. Use to see who keeps coming back, or to find a visitor_id for get-visitor-detail.', 'kantan-analytics' ),
			'callback'      => 'kantan_rest_visitors',
			'params'        => array(
				'days'  => array( 'default' => 30 ),
				'limit' => array( 'default' => 20 ),
			),
			'input_schema'  => kantan_ability_input_schema(
				array(
					'days'  => kantan_ability_days_property(),
					'limit' => kantan_ability_limit_property( 20, 100, __( 'Maximum visitor profiles to return.', 'kantan-analytics' ) ),
				)
			),
			'output_schema' => array(
				'type'        => 'object',
				'description' => $deep_rows_note,
				'properties'  => array(
					'period_days' => array( 'type' => 'integer' ),
					'visitors'    => array( 'type' => 'array' ),
				),
			),
		),

		'get-visitor-detail' => array(
			'label'         => __( 'Get Visitor Detail', 'kantan-analytics' ),
			'description'   => __( 'Returns the full history for one visitor by visitor_id: every session with pages viewed, durations, sources, and conversions. Use after get-visitors or get-realtime to drill into a specific person.', 'kantan-analytics' ),
			'callback'      => 'kantan_rest_get_visitor',
			'params'        => array(
				'visitor_id' => array( 'sanitize' => 'kantan_ability_sanitize_visitor_id' ),
				'days'       => array( 'default' => 90 ),
			),
			'input_schema'  => kantan_ability_input_schema(
				array(
					'visitor_id' => array(
						'type'        => 'string',
						'pattern'     => '^[a-zA-Z0-9_]+$',
						'description' => __( 'Visitor ID exactly as returned by get-visitors.', 'kantan-analytics' ),
					),
					'days'       => kantan_ability_days_property( 90 ),
				),
				array( 'visitor_id' )
			),
			'output_schema' => array(
				'type'        => 'object',
				'description' => $deep_rows_note,
				'properties'  => array(
					'visitor_id'     => array( 'type' => 'string' ),
					'display_name'   => array( 'type' => 'string' ),
					'is_identified'  => array( 'type' => 'boolean' ),
					'total_sessions' => array( 'type' => 'integer' ),
					'first_seen'     => array( 'type' => 'string' ),
					'last_seen'      => array( 'type' => 'string' ),
					'sources'        => array( 'type' => 'string' ),
					'sessions'       => array( 'type' => 'array' ),
				),
			),
		),

		'get-visitor-tech' => array(
			'label'         => __( 'Get Visitor Technology', 'kantan-analytics' ),
			'description'   => __( 'Returns the technology breakdown for a period: device types, browsers, operating systems, and screen resolutions with session counts. Use to understand what visitors browse with (e.g. mobile vs desktop share).', 'kantan-analytics' ),
			'callback'      => 'kantan_rest_user_data',
			'params'        => array(
				'days'  => array( 'default' => 30 ),
				'limit' => array( 'default' => 10 ),
			),
			'input_schema'  => kantan_ability_input_schema(
				array(
					'days'  => kantan_ability_days_property(),
					'limit' => kantan_ability_limit_property( 10, 100, __( 'Maximum rows per breakdown list.', 'kantan-analytics' ) ),
				)
			),
			'output_schema' => array(
				'type'       => 'object',
				'properties' => array(
					'period_days' => array( 'type' => 'integer' ),
					'devices'     => kantan_ability_row_schema(
						array(
							'device_type'  => 'string',
							'sessions'     => 'integer',
							'avg_duration' => 'number',
							'bounce_rate'  => 'number',
						)
					),
					'browsers'    => kantan_ability_row_schema(
						array(
							'browser'  => 'string',
							'sessions' => 'integer',
						)
					),
					'os'          => kantan_ability_row_schema(
						array(
							'os'       => 'string',
							'sessions' => 'integer',
						)
					),
					'resolutions' => kantan_ability_row_schema(
						array(
							'resolution' => 'string',
							'sessions'   => 'integer',
						)
					),
				),
			),
		),

		'get-realtime' => array(
			'label'         => __( 'Get Realtime Visitors', 'kantan-analytics' ),
			'description'   => __( 'Returns who is on the site right now: active visitor count and per-visitor detail within a recent window (default 5 minutes). Use for "who is on the site now" questions.', 'kantan-analytics' ),
			'callback'      => 'kantan_rest_realtime',
			'params'        => array(
				'window' => array( 'default' => 5 ),
			),
			'input_schema'  => kantan_ability_input_schema(
				array(
					'window' => array(
						'type'        => 'integer',
						'minimum'     => 1,
						'maximum'     => 60,
						'default'     => 5,
						'description' => __( 'Activity window in minutes.', 'kantan-analytics' ),
					),
				)
			),
			'output_schema' => array(
				'type'       => 'object',
				'properties' => array(
					'count'          => array( 'type' => 'integer' ),
					'window_minutes' => array( 'type' => 'integer' ),
					'visitors'       => array( 'type' => 'array' ),
					'timestamp'      => array( 'type' => 'string' ),
				),
			),
		),

		'get-interactions' => array(
			'label'         => __( 'Get Click & Scroll Interactions', 'kantan-analytics' ),
			'description'   => __( 'Returns interaction data for a period: total clicks, click distribution across a 3x3 page grid, most-clicked elements, click heatmap points, average scroll depth, and per-page scroll stats. Optionally scoped to one page URL. Use to see what visitors click and how far they scroll.', 'kantan-analytics' ),
			'callback'      => 'kantan_rest_interactions',
			'params'        => array(
				'days'     => array( 'default' => 30 ),
				'page_url' => array(
					'default'  => '',
					'sanitize' => 'esc_url_raw',
				),
			),
			'input_schema'  => kantan_ability_input_schema(
				array(
					'days'     => kantan_ability_days_property(),
					'page_url' => array(
						'type'        => 'string',
						'description' => __( 'Optional. Restrict results to one page URL (as returned by get-top-pages). Empty = site-wide.', 'kantan-analytics' ),
					),
				)
			),
			'output_schema' => array(
				'type'       => 'object',
				'properties' => array(
					'period_days'         => array( 'type' => 'integer' ),
					'total_clicks'        => array( 'type' => 'integer' ),
					'click_regions'       => array(
						'type'        => 'object',
						'description' => __( 'Clicks per 3x3 page region, keyed 1 (top-left) to 9 (bottom-right).', 'kantan-analytics' ),
					),
					'top_clicked'         => kantan_ability_row_schema(
						array(
							'element_tag'  => 'string',
							'element_text' => 'string',
							'clicks'       => 'integer',
							'page_count'   => 'integer',
						)
					),
					'pages_with_clicks'   => kantan_ability_row_schema(
						array(
							'page_url' => 'string',
							'clicks'   => 'integer',
						)
					),
					'avg_scroll_depth'    => array( 'type' => 'number' ),
					'scroll_distribution' => array( 'type' => 'object' ),
					'scroll_by_page'      => kantan_ability_row_schema(
						array(
							'page_url'   => 'string',
							'page_title' => 'string',
							'avg_scroll' => 'number',
							'views'      => 'integer',
						)
					),
					'click_heatmap'       => array( 'type' => 'array' ),
				),
			),
		),

		'get-videos' => array(
			'label'         => __( 'Get Video Engagement', 'kantan-analytics' ),
			'description'   => __( 'Returns engagement for every tracked video in a period: impressions, plays, completions, watch time, play rate, and completion rate, plus site-wide totals. Use for "which videos get watched" questions, or to find a video_key for get-video-detail.', 'kantan-analytics' ),
			'callback'      => 'kantan_rest_get_videos',
			'params'        => array(
				'days' => array( 'default' => 30 ),
			),
			'input_schema'  => kantan_ability_input_schema(
				array( 'days' => kantan_ability_days_property() )
			),
			'output_schema' => array(
				'type'       => 'object',
				'properties' => array(
					'days'   => array( 'type' => 'integer' ),
					'totals' => array( 'type' => 'object' ),
					'videos' => array( 'type' => 'array' ),
				),
			),
		),

		'get-video-detail' => array(
			'label'         => __( 'Get Video Detail', 'kantan-analytics' ),
			'description'   => __( 'Returns full engagement detail for one video by its video_key: daily plays, top pages, all-time stats, the 100-point retention/drop-off curve, and conversion attribution (viewers vs non-viewers). Use after get-videos to see where viewers drop off.', 'kantan-analytics' ),
			'callback'      => 'kantan_rest_get_video_detail',
			'params'        => array(
				'key'  => array( 'sanitize' => 'sanitize_text_field' ),
				'days' => array( 'default' => 30 ),
			),
			'input_schema'  => kantan_ability_input_schema(
				array(
					'key'  => array(
						'type'        => 'string',
						'description' => __( 'The video_key exactly as returned by get-videos (e.g. "youtube:abc123", "vimeo:76979871").', 'kantan-analytics' ),
					),
					'days' => kantan_ability_days_property(),
				),
				array( 'key' )
			),
			'output_schema' => array(
				'type'       => 'object',
				'properties' => array(
					'video_key'   => array( 'type' => 'string' ),
					'provider'    => array( 'type' => 'string' ),
					'video_title' => array( 'type' => 'string' ),
					'duration'    => array( 'type' => 'integer' ),
					'summary'     => array( 'type' => 'object' ),
					'by_day'      => array( 'type' => 'array' ),
					'top_pages'   => array( 'type' => 'array' ),
					'all_time'    => array( 'type' => 'object' ),
					'engagement'  => array( 'type' => 'object' ),
					'attribution' => array( 'type' => 'object' ),
				),
			),
		),

		'get-ai-crawlers' => array(
			'label'         => __( 'Get AI Crawler Activity', 'kantan-analytics' ),
			'description'   => __( 'Returns which AI bots fetch pages on this site: per-crawler hits, pages reached, last seen, and the split between training crawls and live retrieval fetches. This is AI crawlers (bots), distinct from AI referral traffic (humans sent by an assistant) in get-traffic-sources. Use for "who is crawling my site" questions.', 'kantan-analytics' ),
			'callback'      => 'kantan_rest_get_crawlers',
			'params'        => array(
				'days' => array( 'default' => 30 ),
			),
			'input_schema'  => kantan_ability_input_schema(
				array( 'days' => kantan_ability_days_property() )
			),
			'output_schema' => array(
				'type'       => 'object',
				'properties' => array(
					'days'     => array( 'type' => 'integer' ),
					'totals'   => array( 'type' => 'object' ),
					'crawlers' => array( 'type' => 'array' ),
				),
			),
		),

		'get-business-profile' => array(
			'label'         => __( 'Get Business Profile', 'kantan-analytics' ),
			'description'   => __( 'Returns the site\'s Business Profile: the canonical business facts, offerings, audience, and brand voice maintained by the site owner. Read this before drafting anything the user will act on or publish — it is the source of truth for claims. If is_set is false, the profile is empty; tell the user rather than guessing business facts.', 'kantan-analytics' ),
			'callback'      => 'kantan_site_profile_rest_get',
			'params'        => array(),
			'input_schema'  => kantan_ability_input_schema( array() ),
			'output_schema' => array(
				'type'       => 'object',
				'properties' => array(
					'content'    => array(
						'type'        => 'string',
						'description' => __( 'The profile markdown; empty string if unset.', 'kantan-analytics' ),
					),
					'is_set'     => array( 'type' => 'boolean' ),
					'updated_at' => array( 'type' => 'string' ),
					'default'    => array( 'type' => array( 'string', 'null' ) ),
				),
			),
		),

	);
}

/**
 * Register every Kantan Analytics ability from the definitions map.
 */
function kantan_abilities_register() {
	if ( ! function_exists( 'wp_register_ability' ) ) {
		return;
	}

	$readonly_meta = array(
		'annotations'  => array(
			'readonly'    => true,
			'destructive' => false,
			'idempotent'  => true,
		),
		'show_in_rest' => true,
	);

	foreach ( kantan_abilities_get_definitions() as $key => $def ) {
		$callback = $def['callback'];
		$params   = $def['params'];

		wp_register_ability(
			'kantan-analytics/' . $key,
			array(
				'label'               => $def['label'],
				'description'         => $def['description'],
				'category'            => 'analytics',
				'execute_callback'    => function ( $input = null ) use ( $callback, $params ) {
					return kantan_ability_run_rest( $callback, $input, $params );
				},
				'permission_callback' => 'kantan_ability_permission',
				'input_schema'        => $def['input_schema'],
				'output_schema'       => $def['output_schema'],
				'meta'                => $readonly_meta,
			)
		);
	}
}
add_action( 'wp_abilities_api_init', 'kantan_abilities_register' );

/**
 * Expose the Kantan abilities as an MCP server when the WordPress MCP
 * Adapter plugin (wordpress/mcp-adapter) is active.
 *
 * Endpoint: /wp-json/kantan/mcp (HTTP transport, session-based).
 *
 * The tool list is discovered from the abilities registry at init time, so
 * Kantan SEO abilities (kantan-seo/*) automatically appear on this same
 * server when that plugin is active — one connection for the whole suite.
 *
 * @param object $adapter WP\MCP\Core\McpAdapter instance.
 */
function kantan_abilities_register_mcp_server( $adapter ) {
	if ( ! method_exists( $adapter, 'create_server' ) || ! function_exists( 'wp_get_abilities' ) ) {
		return;
	}

	$tools = array();
	foreach ( wp_get_abilities() as $ability ) {
		$name = $ability->get_name();
		if ( 0 === strpos( $name, 'kantan-analytics/' ) || 0 === strpos( $name, 'kantan-seo/' ) ) {
			$tools[] = $name;
		}
	}

	/**
	 * Filters the ability names exposed as MCP tools on the Kantan server.
	 *
	 * @param string[] $tools Ability names.
	 */
	$tools = apply_filters( 'kantan_mcp_tools', $tools );

	if ( empty( $tools ) ) {
		return;
	}

	$adapter->create_server(
		'kantan-analytics',                 // server_id.
		'kantan',                           // REST namespace.
		'mcp',                              // REST route => /wp-json/kantan/mcp.
		__( 'Kantan Analytics', 'kantan-analytics' ),
		__( 'Read-only website analytics and SEO data: visits, pages, traffic sources, conversions, visitors, interactions — plus keywords, health, and AEO when Kantan SEO is active.', 'kantan-analytics' ),
		KANTAN_ANALYTICS_VERSION,
		array( \WP\MCP\Transport\HttpTransport::class ),
		\WP\MCP\Infrastructure\ErrorHandling\ErrorLogMcpErrorHandler::class,
		null,                               // observability handler (default null).
		$tools
	);
}
add_action( 'mcp_adapter_init', 'kantan_abilities_register_mcp_server' );
