import { eq, like } from 'astro:db';
import { Effect, genLogger, pipe, Schema } from '../../../effect.js';
import { AstroDB, type LibSQLClientError } from '../effect/db.js';
import {
	noUndefinedEntries,
	parseData,
	parsedDataResponse,
	type RecursiveSimplifyMutable,
	SelectPluginDataRespondOrFail,
} from '../effect/pluginUtils.js';
import { tsPluginData } from '../tables.js';
import type {
	PluginDataCacheObject,
	PluginDataEntry,
	tsPluginDataInsert,
	tsPluginDataSelect,
	UsePluginDataOpts,
	UsePluginDataOptsBase,
	UserPluginDataOptsImplementation,
	ValidatorOptions,
} from '../types/index.js';
import { CacheContext, isCacheEnabled, isCacheExpired } from '../utils.js';

/**
 * Provides effectful operations for managing plugin-specific data entries in the database.
 *
 * The `SDKCore_PLUGINS` service exposes utilities for plugins to store, retrieve, and update
 * their own data entries, scoped by a unique `pluginId` and `entryId`. All operations are
 * effectful and designed to be used within an Effect context.
 *
 * @remarks
 * - Depends on the `AstroDB` service for database access.
 * - All methods are effectful and yield results or errors as Effects.
 *
 * @example
 * ```typescript
 * const plugins = yield* SDKCore_PLUGINS;
 * const pluginDataOps = yield* plugins.usePluginData('myPlugin', 'entry1');
 * const data = yield* pluginDataOps.select<MyDataType>();
 * ```
 *
 * @service
 * @module studiocms/sdk/SDKCore/modules/plugins
 */
export class SDKCore_PLUGINS extends Effect.Service<SDKCore_PLUGINS>()(
	'studiocms/sdk/SDKCore/modules/plugins',
	{
		dependencies: [AstroDB.Default],
		effect: genLogger('studiocms/sdk/SDKCore/modules/plugins/effect')(function* () {
			const [dbService, { pluginData }] = yield* Effect.all([AstroDB, CacheContext]);

			/**
			 * Provides a set of database operations for managing plugin data entries.
			 */
			const _db = {
				/**
				 * Creates a batch request function for querying plugin data from the database.
				 *
				 * The returned function accepts an object with `batchSize` and `offset` properties,
				 * and performs a database query to select a batch of plugin data records with the specified
				 * limit and offset.
				 */
				batchRequest: dbService.makeQuery((query, o: { batchSize: number; offset: number }) =>
					query((db) => db.select().from(tsPluginData).limit(o.batchSize).offset(o.offset))
				),
				/**
				 * Retrieves plugin data entries from the database whose IDs match the specified plugin ID prefix.
				 */
				getEntriesPluginData: dbService.makeQuery((query, pluginId: string) =>
					query((db) =>
						db
							.select()
							.from(tsPluginData)
							.where(like(tsPluginData.id, `${pluginId}-%`))
					)
				),
				/**
				 * Executes a database query to select a single plugin data entry by its ID.
				 */
				selectPluginDataEntry: dbService.makeQuery((query, id: string) =>
					query((db) => db.select().from(tsPluginData).where(eq(tsPluginData.id, id)).get())
				),
				/**
				 * Inserts a new plugin data entry into the database and returns the inserted record.
				 */
				insertPluginDataEntry: dbService.makeQuery((query, data: tsPluginDataInsert) =>
					query((db) => db.insert(tsPluginData).values(data).returning().get())
				),
				/**
				 * Updates an existing plugin data entry in the database.
				 */
				updatePluginDataEntry: dbService.makeQuery((query, data: tsPluginDataSelect) =>
					query((db) =>
						db.update(tsPluginData).set(data).where(eq(tsPluginData.id, data.id)).returning().get()
					)
				),
			};

			/**
			 * Initializes the plugin data cache by loading entries from the database in batches.
			 *
			 * This generator function retrieves plugin data from the database using a fixed batch size,
			 * and populates the `pluginData` cache with each entry. All cached entries share a single
			 * timestamp indicating when the cache was last updated. The function continues fetching and
			 * caching entries until no more entries are returned from the database.
			 *
			 * @remarks
			 * - Uses a default batch size of 100 entries per database query to efficiently handle large datasets.
			 * - Each cache entry is stored as a tuple containing the entry data and the shared timestamp.
			 * - Intended to be used as an effect within an effectful programming model.
			 *
			 * @yields {void} Yields control to the effect system for each database operation.
			 */
			const initPluginDataCache = Effect.fn(
				'studiocms/sdk/SDKCore/modules/plugins/effect/initPluginDataCache'
			)(function* (BATCH_SIZE?: number) {
				let batchSize = BATCH_SIZE || 100; // Default batch size if not provided
				if (batchSize <= 0) {
					batchSize = 100; // Ensure a positive batch size
				}
				let offset = 0;
				const sharedTimestamp = new Date(); // Single timestamp for all entries

				while (true) {
					const entries = yield* _db.batchRequest({ batchSize, offset });

					if (entries.length === 0) break;

					// Batch insert into cache
					const cacheEntries = entries.map(
						(entry) => [entry.id, { data: entry, lastCacheUpdate: sharedTimestamp }] as const
					);

					// Use Map constructor or batch set operations
					for (const [id, cacheData] of cacheEntries) {
						pluginData.set(id, cacheData);
					}

					offset += batchSize;
				}
			});

			/**
			 * Attempts to clear the plugin data cache using the `pluginData.clear()` method.
			 * If an error occurs during the cache clearing process, it logs the error to the console
			 * and returns a new `Error` instance with a descriptive message.
			 *
			 * @returns {Effect<unknown, Error, void>} An Effect that represents the attempt to clear the plugin data cache,
			 * resolving to `void` on success or an `Error` on failure.
			 */
			const clearPluginDataCache = (): Effect.Effect<void, Error, never> =>
				Effect.try({
					try: () => pluginData.clear(),
					catch: () => new Error('Failed to clear plugin data cache'),
				});

			/**
			 * Retrieves a plugin data entry by its ID, utilizing a cache if enabled.
			 *
			 * - If caching is enabled and the entry exists in the cache and is not expired, returns the cached data.
			 * - If the entry is not in the cache or the cache is expired, fetches fresh data from the database,
			 *   updates the cache, and returns the fresh data.
			 * - If caching is not enabled, always fetches the latest data from the database.
			 *
			 * @param id - The unique identifier of the plugin data entry to retrieve.
			 * @returns An object containing the plugin data entry, or `undefined` if not found.
			 */
			const _selectPluginDataEntry = Effect.fn(
				'studiocms/sdk/SDKCore/modules/plugins/effect/_selectPluginDataEntry'
			)(function* (id: string) {
				if (yield* isCacheEnabled) {
					// Check the cache for the plugin data entry
					const cached = pluginData.get(id);
					if (cached && !isCacheExpired(cached)) {
						const { data: cacheData } = cached;
						// If the entry is found in the cache and is not expired, return it
						return cacheData;
					}

					// If the entry is not found in the cache or is expired, query the database
					// and update the cache with the new data
					const fresh = yield* _db.selectPluginDataEntry(id);
					if (fresh) {
						pluginData.set(id, {
							data: fresh,
							lastCacheUpdate: new Date(),
						});
					}
					return fresh;
				}

				// If caching is not enabled, directly query the database
				// This ensures that we always get the latest data from the database
				return yield* _db.selectPluginDataEntry(id);
			});

			/**
			 * Inserts a new plugin data entry into the database and updates the cache if enabled.
			 *
			 * @param data - The plugin data to insert.
			 * @returns The newly inserted plugin data entry.
			 *
			 * @remarks
			 * If caching is enabled, this function will update the in-memory cache with the new data,
			 * ensuring cache consistency with the database.
			 */
			const _insertPluginDataEntry = Effect.fn(
				'studiocms/sdk/SDKCore/modules/plugins/effect/_insertPluginDataEntry'
			)(function* (data: tsPluginDataInsert) {
				// Insert the plugin data entry into the database
				const newData = yield* _db.insertPluginDataEntry(data);

				// If caching is enabled, update the cache with the new data
				// This ensures that the cache is always in sync with the database
				if (yield* isCacheEnabled) {
					pluginData.set(newData.id, {
						data: newData,
						lastCacheUpdate: new Date(),
					});
				}

				// Return the newly inserted data
				return newData;
			});

			/**
			 * Updates a plugin data entry in the database and, if caching is enabled, updates the cache with the new data.
			 *
			 * @param data - The plugin data entry to update.
			 * @returns The updated plugin data entry.
			 *
			 * @remarks
			 * This function performs the update operation using `_dbUpdatePluginDataEntry`. If caching is enabled,
			 * it also updates the in-memory cache with the new data and the current timestamp.
			 */
			const _updatePluginDataEntry = Effect.fn(
				'studiocms/sdk/SDKCore/modules/plugins/effect/_updatePluginDataEntry'
			)(function* (data: tsPluginDataSelect) {
				// Update the plugin data entry in the database
				const updatedData = yield* _db.updatePluginDataEntry(data);

				// If caching is enabled, update the cache with the new data
				if (yield* isCacheEnabled) {
					pluginData.set(updatedData.id, {
						data: updatedData,
						lastCacheUpdate: new Date(),
					});
				}

				// Return the updated data
				return updatedData;
			});

			/**
			 * Processes a plugin data cache entry, validating and updating it as necessary.
			 *
			 * This generator function checks if the cache entry is associated with the specified plugin,
			 * determines if the cache is expired, and if so, fetches the latest data from the database.
			 * It validates the data using the provided validator, updates the cache if needed, and returns
			 * a parsed data response. If the cache is not expired, it returns the validated cached data.
			 *
			 * @template T - The type of the data object to validate and return.
			 * @param {[string, PluginDataCacheObject]} param0 - A tuple containing the cache key and the cache object.
			 * @param {string} pluginId - The ID of the plugin to filter cache entries.
			 * @param {ValidatorOptions<T>} [validator] - Optional validator options for data validation.
			 * @returns {Effect<unknown, unknown, (ParsedDataResponse<T> | undefined)>} The parsed data response for the entry, or undefined if not applicable.
			 */
			const _processEntryFromCache = Effect.fn(function* <
				T extends Schema.Struct<Schema.Struct.Fields> | object,
			>(
				[key, { data: entry, lastCacheUpdate }]: [string, PluginDataCacheObject],
				pluginId: string,
				validator?: ValidatorOptions<T>
			) {
				// If the key does not start with the pluginId, skip it
				// This ensures that we only process entries related to the specified plugin
				if (!key.startsWith(pluginId)) {
					return undefined;
				}

				if ((yield* isCacheEnabled) && isCacheExpired({ lastCacheUpdate })) {
					// If the cache is expired, we need to fetch the latest data from the database
					const freshEntry = yield* _db.selectPluginDataEntry(entry.id);

					// If the entry is not found in the database, we can skip it
					if (!freshEntry) {
						yield* Effect.log(`Removing stale cache entry: ${entry.id}`);
						pluginData.delete(entry.id);
						return undefined;
					}

					// Validate the fresh entry data
					// This ensures that we always return valid data
					const validated = yield* parseData<T>(freshEntry.data, validator);

					// If the entry is found in the database, update the cache
					pluginData.set(entry.id, {
						data: freshEntry,
						lastCacheUpdate: new Date(),
					});

					// Return the parsed data response for the entry
					// This ensures that we always return the most up-to-date data
					return yield* parsedDataResponse<T>(entry.id, validated);
				}

				// If the entry is not expired or not found in the database, return the cached data
				const validated = yield* parseData<T>(entry.data, validator);
				return yield* parsedDataResponse<T>(entry.id, validated);
			});

			/**
			 * Processes a plugin data entry retrieved from the database.
			 *
			 * @template T - The expected shape of the validated data object.
			 * @param entry - The plugin data entry to process.
			 * @param validator - Optional validator options to validate the entry's data.
			 * @yields The validated data after parsing.
			 * @yields Updates the cache with the entry if caching is enabled.
			 * @returns The parsed data response for the entry.
			 */
			const _processEntryFromDB = Effect.fn(function* <
				T extends Schema.Struct<Schema.Struct.Fields> | object,
			>(entry: tsPluginDataSelect, validator?: ValidatorOptions<T>) {
				// Validate the data for each entry
				const validated = yield* parseData<T>(entry.data, validator);

				// If caching is not enabled, we do not update the cache
				if (yield* isCacheEnabled) {
					// If caching is enabled, update the cache with the new data
					pluginData.set(entry.id, {
						data: entry,
						lastCacheUpdate: new Date(),
					});
				}

				// Return the parsed data response for the entry
				return yield* parsedDataResponse<T>(entry.id, validated);
			});

			/**
			 * Retrieves plugin data entries for a given plugin ID, with optional validation.
			 *
			 * This function first attempts to retrieve entries from the in-memory cache if caching is enabled.
			 * - If cached entries are found and not expired, they are validated and returned.
			 * - If cached entries are expired, the latest data is fetched from the database, validated, and the cache is updated.
			 * - If no entries are found in the cache, all entries are fetched from the database, validated, and cached.
			 *
			 * If caching is not enabled, all entries are fetched directly from the database and validated.
			 *
			 * @typeParam T - The expected shape of the plugin data after validation.
			 * @param pluginId - The unique identifier for the plugin whose entries are to be retrieved.
			 * @param validator - (Optional) Validation options to apply to each entry's data.
			 * @param filter - (Optional) A callback function to filter the entries after retrieval.
			 * @returns An Effect yielding an array of validated and parsed plugin data responses.
			 */
			const _getEntries = Effect.fn('studiocms/sdk/SDKCore/modules/plugins/effect/_getEntries')(
				function* <T extends Schema.Struct<Schema.Struct.Fields> | object>(
					pluginId: string,
					validator?: ValidatorOptions<T>,
					filter?: (data: PluginDataEntry<T>[]) => PluginDataEntry<T>[]
				) {
					if (yield* isCacheEnabled) {
						const data = yield* pipe(
							pluginData.entries(),
							Effect.forEach((entry) => _processEntryFromCache<T>(entry, pluginId, validator)),
							Effect.map(noUndefinedEntries)
						);

						// If we have valid data from the cache, return it
						if (data.length > 0) return data;
					}

					// Make initial todo to fetch entries from the database and process them
					const todo = pipe(
						_db.getEntriesPluginData(pluginId),
						Effect.flatMap(Effect.forEach((entry) => _processEntryFromDB<T>(entry, validator)))
					);

					// If a filter callback is provided, apply it to the todo result
					if (filter) return yield* todo.pipe(Effect.map(filter));

					// Otherwise, return the todo result directly
					return yield* todo;
				}
			);

			/**
			 * Checks if plugin data with the specified ID exists and responds or fails based on the `shouldFail` flag.
			 *
			 * @param id - The unique identifier of the plugin data to check.
			 * @param shouldFail - Determines the behavior when plugin data with the given ID exists:
			 *   - If `true` and the data exists, the effect fails with an error.
			 *   - If `false` and the data does not exist, returns `false`.
			 *   - Otherwise, returns `true`.
			 * @returns
			 *   - Fails with an error if the data exists and `shouldFail` is `true`.
			 *   - Returns `false` if the data does not exist and `shouldFail` is `false`.
			 *   - Returns `true` otherwise.
			 */
			const _selectPluginDataEntryRespondOrFail = Effect.fn(
				'studiocms/sdk/SDKCore/modules/plugins/effect/_selectPluginDataEntryRespondOrFail'
			)(function* (id: string, mode: SelectPluginDataRespondOrFail) {
				// Check if the plugin data with the given ID exists
				const existing = yield* _selectPluginDataEntry(id);

				// If the plugin data exists, we handle it based on the mode and shouldFail flag
				// If it does not exist, we handle it based on the mode and shouldFail
				switch (mode) {
					case SelectPluginDataRespondOrFail.ExistsNoFail: {
						// If it exists, return the existing data
						if (existing) return existing;
						// If it does not exist, return undefined
						return undefined;
					}
					case SelectPluginDataRespondOrFail.ExistsShouldFail: {
						// If it exists, fail with an error
						if (existing)
							return yield* Effect.fail(new Error(`Plugin data with ID ${id} already exists.`));
						// If it does not exist, return undefined
						return undefined;
					}
					case SelectPluginDataRespondOrFail.NotExistsShouldFail: {
						// If it does not exist, fail with an error
						if (!existing)
							return yield* Effect.fail(new Error(`Plugin data with ID ${id} does not exist.`));
						// If it exists, return undefined
						return undefined;
					}
					default:
						return yield* Effect.fail(new Error(`Invalid mode: ${mode}`));
				}
			});

			/**
			 * Selects and parses plugin data for a given entry ID.
			 *
			 * This generator function attempts to retrieve plugin data associated with the specified `generatedEntryId`.
			 * - If the data exists, it validates and parses the data using the provided `validator` (if any), and returns the parsed data response.
			 * - If the data does not exist, it returns `undefined` without throwing an error, making it suitable for optional plugin data scenarios.
			 *
			 * @typeParam T - The expected schema or object type for the plugin data.
			 * @param generatedEntryId - The unique identifier for the plugin data entry to select.
			 * @param validator - (Optional) Validation options or schema for parsing the plugin data.
			 * @returns The parsed plugin data response of type `T`, or `undefined` if the entry does not exist.
			 */
			const _select = Effect.fn(
				'studiocms/sdk/SDKCore/modules/plugins/effect/usePluginData.select'
			)(function* <T extends Schema.Struct<Schema.Struct.Fields> | object>(
				generatedEntryId: string,
				validator?: ValidatorOptions<T>
			) {
				// Check if the plugin data with the given ID exists
				// If it exists, proceed to validate and parse the data
				// If it does not exist, return undefined
				// This ensures that we only attempt to parse existing data
				// and do not throw an error when the data is not found
				// This is useful for cases where the plugin data is optional
				const existing = yield* _selectPluginDataEntryRespondOrFail(
					generatedEntryId,
					SelectPluginDataRespondOrFail.ExistsNoFail
				);

				// If it does not exist, return undefined
				if (!existing) return undefined;

				// Validate and parse the existing data
				const data = yield* parseData<T>(existing.data, validator);

				// Return the parsed data response for the existing entry
				return yield* parsedDataResponse<T>(generatedEntryId, data);
			});

			/**
			 * Inserts a new plugin data entry into the database.
			 *
			 * This function first checks if an entry with the given `generatedEntryId` already exists.
			 * If it does, the operation fails to ensure uniqueness and prevent duplicate entries.
			 * If not, it validates the provided data using the optional `validator`, then inserts the new entry.
			 * The inserted data is returned in a parsed response format.
			 *
			 * @template T - The type of the data to insert, which can be a Schema.Struct or a plain object.
			 * @param generatedEntryId - The unique identifier for the plugin data entry.
			 * @param data - The data to be inserted.
			 * @param validator - Optional validation options for the data.
			 * @returns The inserted and parsed plugin data entry.
			 * @throws If an entry with the given ID already exists or if validation fails.
			 */
			const _insert = Effect.fn(
				'studiocms/sdk/SDKCore/modules/plugins/effect/usePluginData.insert'
			)(function* <T extends Schema.Struct<Schema.Struct.Fields> | object>(
				generatedEntryId: string,
				data: T,
				validator?: ValidatorOptions<T>
			) {
				// Check if the plugin data with the given ID already exists
				// If it exists, fail with an error
				// This ensures that we do not accidentally insert duplicate entries
				// and maintain the uniqueness of the plugin data entries
				// If it does not exist, proceed to insert the new data
				yield* _selectPluginDataEntryRespondOrFail(
					generatedEntryId,
					SelectPluginDataRespondOrFail.ExistsShouldFail
				);

				// Validate the data before inserting
				const parsedData = yield* parseData<T>(data, validator);

				// Insert the new plugin data into the database
				// Note: The 'id' field is expected to be unique, so we use
				// it as the primary key in the table definition.
				const inserted = yield* _insertPluginDataEntry({
					id: generatedEntryId,
					data: parsedData,
				});

				// Return the inserted data
				return yield* parsedDataResponse<T>(inserted.id, parsedData);
			});

			/**
			 * Updates an existing plugin data entry by its generated ID.
			 *
			 * This function performs the following steps:
			 * 1. Checks if the plugin data entry with the specified ID exists.
			 *    - If it does not exist, the operation fails to prevent accidental creation.
			 * 2. Optionally validates the provided data using the given validator options.
			 * 3. Updates the existing plugin data entry in the database with the validated data.
			 * 4. Returns a parsed data response for the updated record.
			 *
			 * @template T - The shape of the data to update, either a Schema.Struct or a plain object.
			 * @param generatedEntryId - The unique identifier of the plugin data entry to update.
			 * @param data - The new data to update the entry with.
			 * @param validator - (Optional) Validator options to validate the data before updating.
			 * @returns The parsed data response for the updated plugin data entry.
			 * @throws If the plugin data entry does not exist or if validation fails.
			 */
			const _update = Effect.fn(
				'studiocms/sdk/SDKCore/modules/plugins/effect/usePluginData.update'
			)(function* <T extends Schema.Struct<Schema.Struct.Fields> | object>(
				generatedEntryId: string,
				data: T,
				validator?: ValidatorOptions<T>
			) {
				// Check if the plugin data with the given ID exists
				// If it does not exist, fail with an error
				// This ensures that we only update existing records
				// and prevents accidental creation of new records
				// when trying to update non-existing data
				yield* _selectPluginDataEntryRespondOrFail(
					generatedEntryId,
					SelectPluginDataRespondOrFail.NotExistsShouldFail
				);

				// Validate the data before updating
				const parsedData = yield* parseData<T>(data, validator);

				// Update the existing plugin data in the database
				const updated = yield* _updatePluginDataEntry({
					id: generatedEntryId,
					data: parsedData,
				});

				// Return the parsed data response for the updated record
				return yield* parsedDataResponse<T>(updated.id, parsedData);
			});

			const buildReturn = <T extends Schema.Struct<Schema.Struct.Fields> | object>(
				pluginId: string,
				entryId: string,
				validator?: ValidatorOptions<T>
			) => {
				const generatedEntryId = `${pluginId}-${entryId}`;
				return {
					/**
					 * Generates a unique ID for the plugin data entry.
					 *
					 * @returns An Effect that yields the generated ID. In the format `${pluginId}-${entryId}`
					 */
					generatedId: () => Effect.succeed(generatedEntryId),

					/**
					 * Selects a plugin data entry by its ID, validating the data if a validator is provided.
					 *
					 * @returns An Effect that yields the selected plugin data entry or `undefined` if not found.
					 */
					select: () => _select<T>(generatedEntryId, validator),

					/**
					 * Inserts new plugin data into the database after validating the input.
					 *
					 * @param data - The plugin data to insert.
					 * @yields Throws an error if validation fails or if the entry already exists.
					 * @returns The parsed data response for the inserted entry.
					 */
					insert: (data: T) => _insert<T>(generatedEntryId, data, validator),

					/**
					 * Updates existing plugin data in the database after validating the input.
					 *
					 * @param data - The updated plugin data.
					 * @yields Throws an error if validation fails.
					 * @returns The parsed data response for the updated entry.
					 */
					update: (data: T) => _update<T>(generatedEntryId, data, validator),
				};
			};

			// Function overloads for `usePluginData` to handle different cases:
			// This function provides a set of effectful operations for managing plugin data entries.
			// It can be called with just a pluginId to retrieve all entries,
			// or with both pluginId and entryId to perform CRUD operations on a specific entry.
			// This allows for flexible usage depending on whether the user wants to
			// manage all entries for a plugin or a specific entry.

			/**
			 * Retrieves all plugin data entries for a given plugin ID.
			 */
			function usePluginData<
				T extends Schema.Struct<Schema.Struct.Fields> | object,
				// biome-ignore lint/suspicious/noExplicitAny: This is a generic type for the plugin data.
				R extends object = T extends Schema.Struct<any> ? RecursiveSimplifyMutable<T['Type']> : T,
			>(
				pluginId: string,
				opts?: UsePluginDataOptsBase<T>
			): {
				getEntries: (
					filter?: (data: PluginDataEntry<R>[]) => PluginDataEntry<R>[]
				) => Effect.Effect<PluginDataEntry<R>[], LibSQLClientError | Error, never>;
				getEntry: (id: string) => {
					generatedId: () => Effect.Effect<string, never, never>;
					select: () => Effect.Effect<
						PluginDataEntry<R> | undefined,
						LibSQLClientError | Error,
						never
					>;
					insert: (data: R) => Effect.Effect<PluginDataEntry<R>, LibSQLClientError | Error, never>;
					update: (data: R) => Effect.Effect<PluginDataEntry<R>, LibSQLClientError | Error, never>;
				};
			};

			/**
			 * Retrieves or manipulates plugin data entries for a specific plugin ID and entry ID.
			 *
			 * @param pluginId - The unique identifier for the plugin.
			 * @param entryId - (Optional) The unique identifier for the plugin data entry.
			 * @returns An object with methods to manage plugin data entries.
			 */
			function usePluginData<
				T extends Schema.Struct<Schema.Struct.Fields> | object,
				// biome-ignore lint/suspicious/noExplicitAny: This is a generic type for the plugin data.
				R extends object = T extends Schema.Struct<any> ? RecursiveSimplifyMutable<T['Type']> : T,
			>(
				pluginId: string,
				opts?: UsePluginDataOpts<T>
			): {
				generatedId: () => Effect.Effect<string, never, never>;
				select: () => Effect.Effect<
					PluginDataEntry<R> | undefined,
					LibSQLClientError | Error,
					never
				>;
				insert: (data: R) => Effect.Effect<PluginDataEntry<R>, LibSQLClientError | Error, never>;
				update: (data: R) => Effect.Effect<PluginDataEntry<R>, LibSQLClientError | Error, never>;
			};

			/**
			 * Implementation of the `usePluginData` function that provides access to plugin data entries.
			 */
			function usePluginData<T extends Schema.Struct<Schema.Struct.Fields> | object>(
				pluginId: string,
				{ entryId, validator }: UserPluginDataOptsImplementation<T> = {}
			) {
				if (!entryId) {
					return {
						/**
						 * Retrieves all plugin data entries for the specified plugin ID.
						 *
						 * @template T - The type of the plugin data object.
						 * @param validator - Optional validator options for validating the plugin data.
						 * @returns An Effect that yields an array of `PluginDataEntry<T>` objects.
						 */
						getEntries: (filter?: (data: PluginDataEntry<T>[]) => PluginDataEntry<T>[]) =>
							_getEntries<T>(pluginId, validator, filter),
						getEntry: (id: string) => buildReturn<T>(pluginId, id, validator),
					};
				}
				return buildReturn<T>(pluginId, entryId, validator);
			}

			/**
			 * Utility class to infer types from a given Schema.
			 *
			 * @typeParam S - The schema type extending `Schema.Struct<any>`.
			 *
			 * @property _Schema - The schema instance used for type inference.
			 * @property usePluginData - The inferred type from the schema, used for plugin data.
			 * @property Insert - A recursively simplified, mutable version of the schema's type.
			 *
			 */
			class InferType<
				// biome-ignore lint/suspicious/noExplicitAny: as this is a generic type for the plugin data.
				S extends Schema.Struct<any>,
				R = RecursiveSimplifyMutable<S['Type']>,
			> {
				readonly _Schema: S;
				readonly $UsePluginData!: S;
				readonly $Insert!: R;
				constructor(schema: S) {
					if (!schema || !Schema.isSchema(schema)) {
						throw new Error('InferType requires a valid Schema.Struct instance.');
					}
					this._Schema = schema;
				}
			}

			return {
				/**
				 * Provides a set of effectful operations for managing plugin data entries by plugin ID and optional entry ID.
				 *
				 * When an `entryId` is provided, returns an object with methods to:
				 * - Generate a unique plugin data entry ID.
				 * - Insert new plugin data after validation and duplicate checks.
				 * - Select and validate existing plugin data by ID.
				 * - Update existing plugin data after validation.
				 *
				 * When no `entryId` is provided, returns an object with a method to retrieve all entries for the given plugin.
				 *
				 * @param pluginId - The unique identifier for the plugin.
				 * @param entryId - (Optional) The unique identifier for the plugin data entry.
				 * @returns An object with effectful methods for plugin data management, varying by presence of `entryId`.
				 */
				usePluginData,

				/**
				 * Initializes the plugin data cache by fetching all existing entries from the database
				 * and populating the in-memory cache with these entries.
				 */
				initPluginDataCache,

				/**
				 * Clears the plugin data cache, removing all cached entries.
				 *
				 * @returns An Effect that resolves to `void` on success or an `Error` on failure.
				 */
				clearPluginDataCache,

				/**
				 * Utility class to infer types from a given Schema.
				 *
				 * @typeParam S - The schema type extending `Schema.Struct<any>`.
				 *
				 * @property _Schema - The schema instance used for type inference.
				 * @property usePluginData - The inferred type from the schema, used for plugin data.
				 * @property Insert - A recursively simplified, mutable version of the schema's type.
				 *
				 */
				InferType,
			};
		}),
	}
) {}
