import type { APIContext, APIRoute, MiddlewareNext } from 'astro';
import type { Effect } from 'effect';

/**
 * Represents a middleware handler function for processing API requests within the Effect system.
 *
 * @param context - The API context object containing request and environment information.
 * @param next - The next middleware function in the chain.
 * @returns An Effect that resolves to either a Response or a Promise of Response.
 */
export type EffectMiddlewareHandler = (
	context: APIContext,
	next: MiddlewareNext
) => Effect.Effect<Promise<Response> | Response, unknown, never>;

/**
 * Represents an entry in the middleware router configuration.
 *
 * @property includePaths - Optional path(s) to include for this middleware. Can be a string or an array of strings.
 * @property excludePaths - Optional path(s) to exclude from this middleware. Can be a string or an array of strings.
 * @property priority - Optional priority for the middleware, determining the order of execution. Lower numbers execute first.
 * @property handler - The middleware handler function to execute for matched paths.
 */
export interface EffectMiddlewareRouterEntry {
	includePaths?: string | string[];
	excludePaths?: string | string[];
	priority?: number;
	handler: EffectMiddlewareHandler;
}

/**
 * Represents a handler function for an API route using the Effect system.
 *
 * @param context - The API context provided to the route handler.
 * @returns An Effect that resolves to a `Response` or a `Promise<Response>`.
 */
export type EffectAPIRouteHandler = (
	context: APIContext
) => Effect.Effect<Promise<Response> | Response, unknown, never>;

/**
 * Options for configuring the response of an API endpoint.
 *
 * @property allowedOrigins - An optional array of allowed origin URLs for CORS.
 * @property headers - An optional record of additional HTTP headers to include in the response.
 */
export interface AllResponseOpts {
	allowedOrigins?: string[];
	headers?: HeadersInit;
}

/**
 * Extends {@link AllResponseOpts} to include HTTP methods allowed for the response.
 *
 * @remarks
 * This interface is typically used to specify additional options for API responses,
 * including which HTTP methods are permitted.
 *
 * @extends AllResponseOpts
 *
 * @property allowedMethods - An array of strings representing the HTTP methods that are allowed (e.g., 'GET', 'POST').
 */
export interface OptionsResponseOpts extends AllResponseOpts {
	allowedMethods: string[];
}

/**
 * Options for creating a response object, extending all base response options.
 *
 * @extends AllResponseOpts
 * @property {number} [status] - Optional HTTP status code for the response.
 * @property {string} [statusText] - Optional HTTP status text for the response.
 */
export interface CreateResponseOpts extends AllResponseOpts {
	status?: number;
	statusText?: string;
}

/**
 * Represents the resolved type of the body content from an Astro API request,
 * based on the specified body parsing method.
 *
 * @template T - The type of body parsing method to use. Can be 'json', 'text', or 'formData'.
 *               - 'json': Resolves to the parsed JSON object.
 *               - 'text': Resolves to the request body as a string.
 *               - 'formData': Resolves to a FormData object.
 *
 * This utility type extracts the return type of the corresponding method on `APIContext['request']`
 * and awaits it, providing the actual type you will receive after calling the method.
 *
 * @example
 * type JsonBody = AstroAPIRequestBody<'json'>; // Parsed JSON object
 * type TextBody = AstroAPIRequestBody<'text'>; // string
 * type FormDataBody = AstroAPIRequestBody<'formData'>; // FormData
 */
export type AstroAPIRequestBody<T extends 'json' | 'text' | 'formData'> = Awaited<
	ReturnType<APIContext['request'][T]>
>;

export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';

/**
 * Options for configuring an Effect API route.
 *
 * @property onError - Optional callback invoked when an error occurs during route handling.
 *   Receives the error and the current API context. Should return a `Response` or a promise resolving to a `Response`.
 *
 * @property onSuccess - Optional callback invoked after a successful response is generated.
 *   Receives the response and the current API context. Should return a `Response` or a promise resolving to a `Response`.
 *
 * @property onBeforeEffect - Optional callback invoked before the main effect is executed.
 *   Receives the current API context and can return a modified context or a promise resolving to one.
 *
 * @property cors - Optional CORS configuration for the route.
 *   - `origin`: Allowed origins (string, array of strings, or boolean).
 *   - `methods`: Allowed HTTP methods.
 *   - `headers`: Allowed headers.
 *   - `credentials`: Whether credentials are allowed.
 *
 * @property timeout - Optional timeout in milliseconds for the route handler.
 *
 * @property validate - Optional validation functions for incoming request data.
 *   - `params`: Function to validate route parameters.
 *   - `query`: Function to validate query parameters.
 *   - `body`: Function to validate the request body, parameterized by body type.
 */
export interface EffectRouteOptions {
	onError?: (error: unknown, context: APIContext) => Response | Promise<Response>;
	onSuccess?: (response: Response, context: APIContext) => Response | Promise<Response>;
	onBeforeEffect?: (context: APIContext) => APIContext | Promise<APIContext>;
	cors?: {
		origin?: string | string[] | boolean;
		methods?: ReadonlyArray<HTTPMethod>;
		headers?: string[];
		credentials?: boolean;
	};
	timeout?: number;
	validate?: {
		params?: (params: APIContext['params']) => boolean | Promise<boolean>;
		query?: (query: APIContext['url']['searchParams']) => boolean | Promise<boolean>;
		body?: // biome-ignore lint/suspicious/noExplicitAny: This is the json return type
			| { kind: 'json'; json: (body: any) => boolean | Promise<boolean> }
			| { kind: 'text'; text: (body: string) => boolean | Promise<boolean> }
			| { kind: 'formData'; formData: (body: FormData) => boolean | Promise<boolean> };
	};
}

/**
 * Configuration options for an API route handler.
 *
 * Extends {@link EffectRouteOptions} to provide additional configuration,
 * including the ability to specify method-specific overrides.
 *
 * @property methods - An optional object allowing you to override route options
 *   for specific HTTP methods. Each key corresponds to an HTTP method and its value
 *   is a partial set of {@link EffectRouteOptions} for that method.
 *   Supported methods include: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, and ALL.
 */
export interface RouteHandlerConfig extends EffectRouteOptions {
	methods?: Partial<Record<HTTPMethod | 'ALL', Partial<EffectRouteOptions>>>;
}

/**
 * Represents a collection of HTTP method handlers for an API route.
 * Each property corresponds to a standard HTTP method and is optional.
 *
 * @property GET - Handler for HTTP GET requests.
 * @property POST - Handler for HTTP POST requests.
 * @property PUT - Handler for HTTP PUT requests.
 * @property DELETE - Handler for HTTP DELETE requests.
 * @property PATCH - Handler for HTTP PATCH requests.
 * @property HEAD - Handler for HTTP HEAD requests.
 * @property OPTIONS - Handler for HTTP OPTIONS requests.
 * @property ALL - Handler for all HTTP methods.
 */
export type RouteHandlers = Partial<Record<HTTPMethod | 'ALL', EffectAPIRouteHandler>>;

/**
 * Extracts the defined routes from a collection of route handlers.
 *
 * @template T - The type of the route handlers.
 * @returns An object type where each key corresponds to a defined route handler,
 *   and the value is the corresponding APIRoute type.
 *
 * @remarks
 * This utility type filters out any undefined properties from the provided route handlers,
 * ensuring that only valid routes are included in the resulting type.
 */
export type ExtractDefinedRoutes<T extends RouteHandlers> = {
	[K in keyof T as T[K] extends EffectAPIRouteHandler ? K : never]: APIRoute;
};
