<?php

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

require_once __DIR__ . '/inc/rest-shared.php';
require_once __DIR__ . '/inc/rest-auth.php';

define( '{{phpPrefixUpper}}_DATA_STORAGE_MODE', '{{dataStorageMode}}' );

function {{phpPrefix}}_get_rest_build_dir() {
	return dirname( __DIR__, 3 ) . '/build/blocks/{{slugKebabCase}}';
}

function {{phpPrefix}}_get_counter_table_name() {
	global $wpdb;
	return $wpdb->prefix . '{{phpPrefix}}_counters';
}

function {{phpPrefix}}_get_counter_lock_name( $post_id, $resource_key ) {
	return 'wpt_pcl_' . md5(
		'{{phpPrefix}}|' . (int) $post_id . '|' . (string) $resource_key
	);
}

function {{phpPrefix}}_with_counter_lock( $post_id, $resource_key, $callback ) {
	global $wpdb;

	$lock_name = {{phpPrefix}}_get_counter_lock_name( $post_id, $resource_key );
	$acquired  = (int) $wpdb->get_var(
		$wpdb->prepare(
			'SELECT GET_LOCK(%s, 5)',
			$lock_name
		)
	);

	if ( 1 !== $acquired ) {
		return new WP_Error( 'counter_lock_timeout', 'Could not acquire the counter lock.', array( 'status' => 503 ) );
	}

	try {
		return $callback();
	} finally {
		$wpdb->get_var(
			$wpdb->prepare(
				'SELECT RELEASE_LOCK(%s)',
				$lock_name
			)
		);
	}
}

function {{phpPrefix}}_maybe_install_storage() {
	if ( 'custom-table' !== {{phpPrefixUpper}}_DATA_STORAGE_MODE ) {
		return;
	}

	global $wpdb;
	require_once ABSPATH . 'wp-admin/includes/upgrade.php';

	$table_name      = {{phpPrefix}}_get_counter_table_name();
	$charset_collate = $wpdb->get_charset_collate();
	$sql             = "CREATE TABLE {$table_name} (
		post_id bigint(20) unsigned NOT NULL,
		resource_key varchar(100) NOT NULL,
		count bigint(20) unsigned NOT NULL DEFAULT 0,
		updated_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
		PRIMARY KEY  (post_id, resource_key)
	) {$charset_collate};";

	dbDelta( $sql );
	$table_exists = $wpdb->get_var(
		$wpdb->prepare(
			'SHOW TABLES LIKE %s',
			$table_name
		)
	);

	if ( $table_name === $table_exists ) {
		update_option( '{{phpPrefix}}_storage_version', '1.0.0' );
	}
}

function {{phpPrefix}}_ensure_storage_installed() {
	if ( 'custom-table' === {{phpPrefixUpper}}_DATA_STORAGE_MODE && '1.0.0' !== get_option( '{{phpPrefix}}_storage_version', '' ) ) {
		{{phpPrefix}}_maybe_install_storage();
	}
}

function {{phpPrefix}}_get_counter( $post_id, $resource_key ) {
	global $wpdb;

	if ( 'custom-table' === {{phpPrefixUpper}}_DATA_STORAGE_MODE ) {
		$table_name = {{phpPrefix}}_get_counter_table_name();
		$count      = $wpdb->get_var(
			$wpdb->prepare(
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name comes from an internal helper.
				"SELECT count FROM {$table_name} WHERE post_id = %d AND resource_key = %s",
				$post_id,
				$resource_key
			)
		);

		return null === $count ? 0 : (int) $count;
	}

	$meta_key = '_' . '{{phpPrefix}}' . '_counter_' . sanitize_key( $resource_key );
	return (int) get_post_meta( $post_id, $meta_key, true );
}

function {{phpPrefix}}_increment_counter( $post_id, $resource_key, $delta ) {
	global $wpdb;

	if ( 'custom-table' === {{phpPrefixUpper}}_DATA_STORAGE_MODE ) {
		$table_name    = {{phpPrefix}}_get_counter_table_name();
		$delta_value   = (int) $delta;
		$initial_count = max( 0, $delta_value );
		$result        = $wpdb->query(
			$wpdb->prepare(
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Table name comes from an internal helper.
				"INSERT INTO {$table_name} (post_id, resource_key, count, updated_at)
				VALUES (%d, %s, %d, %s)
				ON DUPLICATE KEY UPDATE
					count = GREATEST(0, count + %d),
					updated_at = VALUES(updated_at)",
				$post_id,
				$resource_key,
				$initial_count,
				current_time( 'mysql', true ),
				$delta_value
			)
		);

		if ( false === $result ) {
			return new WP_Error( 'counter_update_failed', 'Failed to update the counter.', array( 'status' => 500 ) );
		}

		return {{phpPrefix}}_get_counter( $post_id, $resource_key );
	}

	return {{phpPrefix}}_with_counter_lock(
		$post_id,
		$resource_key,
		function () use ( $delta, $post_id, $resource_key ) {
			$meta_key   = '_' . '{{phpPrefix}}' . '_counter_' . sanitize_key( $resource_key );
			$next_count = max( 0, {{phpPrefix}}_get_counter( $post_id, $resource_key ) + (int) $delta );
			update_post_meta( $post_id, $meta_key, $next_count );
			return $next_count;
		}
	);
}

function {{phpPrefix}}_build_state_response( $post_id, $resource_key, $count ) {
	return array(
		'postId'      => (int) $post_id,
		'resourceKey' => (string) $resource_key,
		'count'       => (int) $count,
		'storage'     => {{phpPrefixUpper}}_DATA_STORAGE_MODE,
	);
}

function {{phpPrefix}}_block_tree_has_resource_key( $blocks, $block_name, $resource_key ) {
	if ( ! is_array( $blocks ) ) {
		return false;
	}

	foreach ( $blocks as $block ) {
		if ( ! is_array( $block ) ) {
			continue;
		}

		if ( (string) ( $block['blockName'] ?? '' ) === $block_name ) {
			$attributes = isset( $block['attrs'] ) && is_array( $block['attrs'] ) ? $block['attrs'] : array();
			$candidate_resource_key = array_key_exists( 'resourceKey', $attributes )
				? (string) $attributes['resourceKey']
				: 'primary';
			if ( (string) $resource_key === $candidate_resource_key ) {
				return true;
			}
		}

		if (
			isset( $block['innerBlocks'] ) &&
			{{phpPrefix}}_block_tree_has_resource_key( $block['innerBlocks'], $block_name, $resource_key )
		) {
			return true;
		}
	}

	return false;
}

function {{phpPrefix}}_get_rendered_block_instance_key( $post_id, $block_name, $resource_key ) {
	return 'wpt_pri_' . md5( implode( '|', array( (string) $block_name, (int) $post_id, (string) $resource_key ) ) );
}

function {{phpPrefix}}_record_rendered_block_instance( $post_id, $block_name, $resource_key ) {
	if ( $post_id <= 0 || '' === (string) $resource_key || '' === (string) $block_name ) {
		return;
	}

	set_transient(
		{{phpPrefix}}_get_rendered_block_instance_key( $post_id, $block_name, $resource_key ),
		1,
		5 * MINUTE_IN_SECONDS
	);
}

function {{phpPrefix}}_has_rendered_block_instance( $post_id, $resource_key ) {
	if ( $post_id <= 0 || '' === (string) $resource_key ) {
		return false;
	}

	if (
		false !== get_transient(
			{{phpPrefix}}_get_rendered_block_instance_key(
				$post_id,
				'{{namespace}}/{{slugKebabCase}}',
				$resource_key
			)
		)
	) {
		return true;
	}

	$post = get_post( $post_id );
	if ( ! ( $post instanceof WP_Post ) ) {
		return false;
	}

	return {{phpPrefix}}_block_tree_has_resource_key(
		parse_blocks( (string) $post->post_content ),
		'{{namespace}}/{{slugKebabCase}}',
		(string) $resource_key
	);
}

function {{phpPrefix}}_build_bootstrap_response( $post_id, $resource_key ) {
	$post          = get_post( $post_id );
	$can_read_post =
		$post instanceof WP_Post &&
		(
			is_post_publicly_viewable( $post ) ||
			current_user_can( 'read_post', $post->ID )
		);
	$can_write = $post_id > 0 &&
		is_user_logged_in() &&
		$can_read_post &&
		{{phpPrefix}}_has_rendered_block_instance( (int) $post_id, (string) $resource_key );
	$response  = array(
		'canWrite' => $can_write,
	);

	if ( $can_write ) {
		$response['restNonce'] = wp_create_nonce( 'wp_rest' );
	}

	return $response;
}

function {{phpPrefix}}_handle_get_state( WP_REST_Request $request ) {
	$payload = {{phpPrefix}}_validate_and_sanitize_request(
		array(
			'postId'      => $request->get_param( 'postId' ),
			'resourceKey' => $request->get_param( 'resourceKey' ),
		),
		{{phpPrefix}}_get_rest_build_dir(),
		'state-query',
		'query'
	);

	if ( is_wp_error( $payload ) ) {
		return $payload;
	}

	$count = {{phpPrefix}}_get_counter( (int) $payload['postId'], (string) $payload['resourceKey'] );
	return rest_ensure_response(
		{{phpPrefix}}_build_state_response(
			(int) $payload['postId'],
			(string) $payload['resourceKey'],
			$count
		)
	);
}

function {{phpPrefix}}_handle_get_bootstrap( WP_REST_Request $request ) {
	$payload = {{phpPrefix}}_validate_and_sanitize_request(
		array(
			'postId'      => $request->get_param( 'postId' ),
			'resourceKey' => $request->get_param( 'resourceKey' ),
		),
		{{phpPrefix}}_get_rest_build_dir(),
		'bootstrap-query',
		'query'
	);

	if ( is_wp_error( $payload ) ) {
		return $payload;
	}

	$response = rest_ensure_response(
		{{phpPrefix}}_build_bootstrap_response(
			(int) $payload['postId'],
			(string) $payload['resourceKey']
		)
	);

	$response->header( 'Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0, s-maxage=0' );
	$response->header( 'Pragma', 'no-cache' );
	$response->header( 'Vary', 'Cookie' );
	return $response;
}

function {{phpPrefix}}_handle_write_state( WP_REST_Request $request ) {
	$payload = {{phpPrefix}}_validate_and_sanitize_request(
		$request->get_json_params(),
		{{phpPrefix}}_get_rest_build_dir(),
		'write-state-request',
		'body'
	);

	if ( is_wp_error( $payload ) ) {
		return $payload;
	}

	$count = {{phpPrefix}}_increment_counter(
		(int) $payload['postId'],
		(string) $payload['resourceKey'],
		isset( $payload['delta'] ) ? (int) $payload['delta'] : 1
	);

	if ( is_wp_error( $count ) ) {
		return $count;
	}

	return rest_ensure_response(
		{{phpPrefix}}_build_state_response(
			(int) $payload['postId'],
			(string) $payload['resourceKey'],
			$count
		)
	);
}

function {{phpPrefix}}_register_routes() {
	register_rest_route(
		'{{namespace}}/v1',
		'/{{slugKebabCase}}/state',
		array(
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => '{{phpPrefix}}_handle_get_state',
				'permission_callback' => '__return_true',
			),
			array(
				'methods'             => WP_REST_Server::CREATABLE,
				'callback'            => '{{phpPrefix}}_handle_write_state',
				'permission_callback' => '{{phpPrefix}}_can_write_authenticated',
			),
		)
	);

	register_rest_route(
		'{{namespace}}/v1',
		'/{{slugKebabCase}}/bootstrap',
		array(
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => '{{phpPrefix}}_handle_get_bootstrap',
				'permission_callback' => '__return_true',
			),
		)
	);
}

add_action( 'init', '{{phpPrefix}}_ensure_storage_installed' );
add_action( 'rest_api_init', '{{phpPrefix}}_register_routes' );
