<?php
/**
 * Dashboard Layout: Premium Analytics server-side defaults.
 *
 * Premium Analytics owns its dashboard, so it ships its own default layout
 * rather than relying on the core dashboard endpoint (which is Gutenberg-only
 * and returns the core dashboard's widgets). The default is served on the
 * `dashboardSection` REST shape and a per-dashboard `default-layout` route; the
 * frontend reads it from the section entity, so there is no server-seeded
 * preference.
 *
 * @package automattic/jetpack-premium-analytics
 */

namespace Automattic\Jetpack\PremiumAnalytics;

require_once __DIR__ . '/dashboard-grammar.php';
require_once __DIR__ . '/rest-namespace.php';
// Availability policy for default layout instances: defaults are read outside
// the widget registry bootstrap, so the policy must be loaded here explicitly.
require_once __DIR__ . '/widget-type-support.php';

/**
 * Identifier of the Premium Analytics dashboard, formatted as `<plugin>_<page>`
 * to match the underscore form produced by the wp-build pipeline. Used as the
 * `{name}` segment of the REST route.
 */
const DASHBOARD_NAME = 'jetpack-premium-analytics_dashboard';

/**
 * Filter through which the default layout for a dashboard is resolved.
 */
const DASHBOARD_DEFAULT_LAYOUT_FILTER = 'jetpack_premium_analytics_dashboard_default_layout';

/**
 * Preference keys used by the dashboard route's tabbed sections.
 */
const DASHBOARD_TRAFFIC_SECTION_ID     = 'traffic';
const DASHBOARD_INSIGHTS_SECTION_ID    = 'insights';
const DASHBOARD_SUBSCRIBERS_SECTION_ID = 'subscribers';
const DASHBOARD_STORE_SECTION_ID       = 'store';

/**
 * Resolves the default layout registered for a dashboard.
 *
 * Returns a fresh evaluation of the filter chain each call, so callers always
 * see the current code default rather than a hydrated copy.
 *
 * @param string $dashboard_name Identifier of the dashboard or dashboard section.
 * @return array Array of widget instances (possibly empty).
 */
function get_dashboard_default_layout_for( $dashboard_name ) {
	/**
	 * Filters the default dashboard layout served to users who have not
	 * customized theirs.
	 *
	 * Each entry should match the dashboard's widget instance shape: `uuid`,
	 * `type`, optional `attributes`, optional `placement`.
	 *
	 * @param array  $default_layout Default array of widget instances.
	 * @param string $dashboard_name Identifier of the dashboard or dashboard
	 *                               section receiving the default. Callbacks
	 *                               targeting a specific default should switch
	 *                               on this value.
	 */
	$default = apply_filters( DASHBOARD_DEFAULT_LAYOUT_FILTER, array(), $dashboard_name );

	return remove_unsupported_widget_items(
		is_array( $default ) ? array_values( $default ) : array(),
		'type',
		get_widget_support_context()
	);
}

/**
 * REST callback returning the default layout for the requested dashboard.
 *
 * The route admits every dashboard reader, but its name also resolves to a
 * single tab, so the store tab is refused here the way the section route
 * refuses it through {@see Dashboard_Section::is_available()}. The check keys
 * on the resolved tab rather than the string because two spellings arrive: the
 * bare `store` alias in the URL, and `?name=woocommerce/store`, which WordPress
 * reads ahead of the URL capture.
 *
 * @param \WP_REST_Request $request REST request carrying the dashboard name.
 * @return \WP_REST_Response|\WP_Error Response wrapping the default layout array.
 */
function get_dashboard_default_layout_response( $request ) {
	$dashboard_name = $request['name'];

	if ( DASHBOARD_STORE_SECTION_ID === get_dashboard_default_section_id_for( $dashboard_name )
		&& ! Capabilities::current_user_can_view_store_reports() ) {
		return new \WP_Error(
			'dashboard_section_unavailable',
			__( 'Dashboard section is not available.', 'jetpack-premium-analytics-pkg' ),
			array( 'status' => 404 )
		);
	}

	return rest_ensure_response( get_dashboard_default_layout_for( $dashboard_name ) );
}

/**
 * Registers the REST route that exposes per-dashboard default layouts.
 *
 * @return void
 */
function register_dashboard_default_layout_route() {
	register_rest_route(
		DASHBOARD_REST_NAMESPACE,
		'/dashboards/(?P<name>' . get_dashboard_name_pattern() . ')/default-layout',
		array(
			'methods'             => \WP_REST_Server::READABLE,
			'callback'            => __NAMESPACE__ . '\\get_dashboard_default_layout_response',
			'permission_callback' => array( Capabilities::class, 'current_user_can_view_analytics' ),
			'args'                => array(
				'name' => array(
					'description' => __( 'Dashboard identifier as produced by the build pipeline.', 'jetpack-premium-analytics-pkg' ),
					'type'        => 'string',
				),
			),
		)
	);
}

/**
 * Builds a widget instance for bundled dashboard defaults.
 *
 * @param string $uuid       Widget instance UUID.
 * @param string $type       Widget type.
 * @param int    $order      Widget placement order.
 * @param int    $width      Widget placement width.
 * @param int    $height     Widget placement height.
 * @param array  $attributes Optional widget attributes.
 * @return array Widget instance.
 */
function get_dashboard_default_widget_instance(
	$uuid,
	$type,
	$order,
	$width = 1,
	$height = 1,
	$attributes = array()
) {
	$widget = array(
		'uuid' => $uuid,
		'type' => $type,
	);

	if ( ! empty( $attributes ) ) {
		$widget['attributes'] = $attributes;
	}

	$widget['placement'] = array(
		'width'  => $width,
		'height' => $height,
		'order'  => $order,
	);

	return $widget;
}

/**
 * Returns the bundled default widget layouts keyed by dashboard tab.
 *
 * @return array Map of tab IDs to widget layout arrays.
 */
function get_dashboard_default_section_layouts() {
	return array(
		DASHBOARD_TRAFFIC_SECTION_ID     => array(
			// Rows fill the four-column grid. Plan usage is intentionally not a
			// default; it stays available from the widget picker.
			// Row 1: traffic chart.
			get_dashboard_default_widget_instance(
				'default-traffic-chart-widget-instance',
				'jpa/traffic-chart',
				0,
				4,
				2,
				array(
					'granularity' => 'auto',
				)
			),
			// Row 2: most-viewed posts + referrers + devices.
			get_dashboard_default_widget_instance(
				'default-stats-top-posts-widget-instance',
				'jpa/stats-top-posts',
				1,
				2,
				2,
				array(
					'max' => 10,
				)
			),
			get_dashboard_default_widget_instance(
				'default-referrers-widget-instance',
				'jpa/referrers',
				2,
				1,
				2,
				array(
					'max' => 10,
				)
			),
			get_dashboard_default_widget_instance(
				'default-devices-widget-instance',
				'jpa/devices',
				3,
				1,
				2,
				array(
					'max' => 5,
				)
			),
			// Row 3: locations map + top platforms.
			get_dashboard_default_widget_instance(
				'default-locations-widget-instance',
				'jpa/locations',
				4,
				3,
				2,
				array(
					'max' => 10,
				)
			),
			get_dashboard_default_widget_instance(
				'default-top-platforms-widget-instance',
				'jpa/top-platforms',
				5,
				1,
				2,
				array(
					'max' => 10,
				)
			),
			// Row 4: VideoPress + clicks + authors.
			get_dashboard_default_widget_instance(
				'default-videopress-widget-instance',
				'jpa/videopress',
				6,
				1,
				2,
				array(
					'max' => 7,
				)
			),
			get_dashboard_default_widget_instance(
				'default-clicks-widget-instance',
				'jpa/clicks',
				7,
				1,
				2,
				array(
					'max' => 10,
				)
			),
			get_dashboard_default_widget_instance(
				'default-authors-widget-instance',
				'jpa/authors',
				8,
				2,
				2,
				array(
					'max' => 7,
				)
			),
			// Row 5: UTM insights + search terms + file downloads (Simple only).
			get_dashboard_default_widget_instance(
				'default-utm-insights-widget-instance',
				'jpa/utm-insights',
				9,
				2,
				2,
				array(
					'utmDimension' => 'utm_source,utm_medium',
					'max'          => 10,
				)
			),
			get_dashboard_default_widget_instance(
				'default-search-terms-widget-instance',
				'jpa/search-terms',
				10,
				1,
				2,
				array(
					'max' => 10,
				)
			),
			get_dashboard_default_widget_instance(
				'default-file-downloads-widget-instance',
				'jpa/file-downloads',
				11,
				1,
				2,
				array(
					'max' => 10,
				)
			),
		),
		DASHBOARD_INSIGHTS_SECTION_ID    => array(
			// Follows the prototype's rows (WOOA7S-1786). Emails lives on the
			// Subscribers tab, and the site-wide views heatmap has no widget
			// yet (WOOA7S-1787), so that row is absent.
			// Row 1: highlights banner.
			get_dashboard_default_widget_instance(
				'default-annual-highlights-widget-instance',
				'jpa/annual-highlights',
				0,
				4,
				1
			),
			// Row 2: posting-activity heatmap.
			get_dashboard_default_widget_instance(
				'default-posting-activity-widget-instance',
				'jpa/posting-activity',
				1,
				4,
				1
			),
			// Row 3: the two post spotlights.
			get_dashboard_default_widget_instance(
				'default-latest-post-widget-instance',
				'jpa/latest-post',
				2,
				2,
				2
			),
			get_dashboard_default_widget_instance(
				'default-popular-post-widget-instance',
				'jpa/popular-post',
				3,
				2,
				2
			),
			// Row 4: the period totals + the two most-popular cards. The
			// most-popular cards still crop at this height (WOOA7S-1846).
			get_dashboard_default_widget_instance(
				'default-total-views-widget-instance',
				'jpa/total-views',
				4,
				1,
				1
			),
			get_dashboard_default_widget_instance(
				'default-total-visitors-widget-instance',
				'jpa/total-visitors',
				5,
				1,
				1
			),
			get_dashboard_default_widget_instance(
				'default-most-popular-time-widget-instance',
				'jpa/most-popular-time',
				6,
				1,
				1
			),
			get_dashboard_default_widget_instance(
				'default-most-popular-day-widget-instance',
				'jpa/most-popular-day',
				7,
				1,
				1
			),
			// Row 5: the comment leaderboards, shares, and tags.
			get_dashboard_default_widget_instance(
				'default-most-commented-posts-widget-instance',
				'jpa/most-commented-posts',
				8,
				1,
				2,
				array(
					'max' => 10,
				)
			),
			get_dashboard_default_widget_instance(
				'default-most-commented-authors-widget-instance',
				'jpa/most-commented-authors',
				9,
				1,
				2,
				array(
					'max' => 10,
				)
			),
			get_dashboard_default_widget_instance(
				'default-shares-widget-instance',
				'jpa/shares',
				10,
				1,
				2,
				array(
					'max' => 10,
				)
			),
			get_dashboard_default_widget_instance(
				'default-tags-widget-instance',
				'jpa/tags',
				11,
				1,
				2,
				array(
					'max' => 10,
				)
			),
		),
		DASHBOARD_SUBSCRIBERS_SECTION_ID => array(
			// Subscriber highlights is intentionally not a default: the design
			// opens on the chart. It stays available from the widget picker.
			// Row 1: subscribers chart.
			get_dashboard_default_widget_instance(
				'default-subscribers-chart-widget-instance',
				'jpa/subscribers-chart',
				0,
				4,
				2,
				array(
					'granularity' => 'auto',
				)
			),
			// Row 2: latest subscribers + latest emails sent.
			get_dashboard_default_widget_instance(
				'default-subscribers-list-widget-instance',
				'jpa/subscribers-list',
				1,
				2,
				2,
				array(
					'max' => 6,
				)
			),
			get_dashboard_default_widget_instance(
				'default-subscribers-emails-widget-instance',
				'jpa/stats-emails',
				2,
				2,
				2,
				array(
					'max'    => 10,
					'metric' => 'opens',
				)
			),
		),
		DASHBOARD_STORE_SECTION_ID       => array(
			get_dashboard_default_widget_instance(
				'default-store-performance-widget-instance',
				'jpa/store-performance',
				0,
				2,
				1
			),
			get_dashboard_default_widget_instance(
				'default-total-sales-over-time-widget-instance',
				'jpa/total-sales-over-time',
				1,
				1,
				1
			),
			get_dashboard_default_widget_instance(
				'default-conversion-rate-widget-instance',
				'jpa/conversion-rate',
				2,
				1,
				1
			),
			get_dashboard_default_widget_instance(
				'default-orders-over-time-widget-instance',
				'jpa/orders-over-time',
				3,
				1,
				1
			),
			get_dashboard_default_widget_instance(
				'default-average-order-value-widget-instance',
				'jpa/average-order-value',
				4,
				1,
				1
			),
			get_dashboard_default_widget_instance(
				'default-top-performing-products-widget-instance',
				'jpa/top-performing-products',
				5,
				1,
				1
			),
			get_dashboard_default_widget_instance(
				'default-new-vs-returning-customer-widget-instance',
				'jpa/new-vs-returning-customer',
				6,
				1,
				1
			),
			get_dashboard_default_widget_instance(
				'default-payment-status-widget-instance',
				'jpa/payment-status',
				7,
				1,
				1
			),
			get_dashboard_default_widget_instance(
				'default-orders-fulfillment-widget-instance',
				'jpa/orders-fulfillment',
				8,
				1,
				1
			),
		),
	);
}

/**
 * Resolves a dashboard or section identifier to one of the bundled tab IDs.
 *
 * @param string $dashboard_name Dashboard or section identifier.
 * @return string|null Bundled tab ID, or null when the identifier is unsupported.
 */
function get_dashboard_default_section_id_for( $dashboard_name ) {
	$aliases = array(
		DASHBOARD_NAME                   => DASHBOARD_TRAFFIC_SECTION_ID,
		DASHBOARD_TRAFFIC_SECTION_ID     => DASHBOARD_TRAFFIC_SECTION_ID,
		'analytics/traffic'              => DASHBOARD_TRAFFIC_SECTION_ID,
		DASHBOARD_INSIGHTS_SECTION_ID    => DASHBOARD_INSIGHTS_SECTION_ID,
		'analytics/insights'             => DASHBOARD_INSIGHTS_SECTION_ID,
		DASHBOARD_SUBSCRIBERS_SECTION_ID => DASHBOARD_SUBSCRIBERS_SECTION_ID,
		'analytics/subscribers'          => DASHBOARD_SUBSCRIBERS_SECTION_ID,
		DASHBOARD_STORE_SECTION_ID       => DASHBOARD_STORE_SECTION_ID,
		'woocommerce/store'              => DASHBOARD_STORE_SECTION_ID,
	);

	return $aliases[ $dashboard_name ] ?? null;
}

/**
 * Seeds the bundled default layouts for the Premium Analytics dashboard tabs.
 *
 * Only contributes to the known Premium Analytics dashboard and tab aliases;
 * other dashboards are left untouched so the filter can be reused if more
 * dashboards are added later.
 *
 * @param array  $dashboard_layout Default layout from earlier callbacks.
 * @param string $dashboard_name   Identifier of the dashboard receiving the
 *                                 default.
 * @return array The layout extended with the bundled widget instances.
 */
function seed_default_dashboard_layout( $dashboard_layout, $dashboard_name = '' ) {
	$section_id = get_dashboard_default_section_id_for( $dashboard_name );

	if ( null === $section_id ) {
		return $dashboard_layout;
	}

	$uuids   = array_column( $dashboard_layout, 'uuid' );
	$layouts = get_dashboard_default_section_layouts();

	foreach ( $layouts[ $section_id ] as $widget ) {
		if ( ! in_array( $widget['uuid'], $uuids, true ) ) {
			$dashboard_layout[] = $widget;
			$uuids[]            = $widget['uuid'];
		}
	}

	return $dashboard_layout;
}
add_filter( DASHBOARD_DEFAULT_LAYOUT_FILTER, __NAMESPACE__ . '\\seed_default_dashboard_layout', 10, 2 );
