import { DependencyLifecycle } from '../../types';
import { ResponseCacheConfig } from '../../types/service-response-store';
import { KosFetchOptions } from '../../types/utils/kos-fetch';
import { ClientBody, ClientParams } from '../../util/kos-service-request';
import { ServiceRequestError } from '../../util/service-request-error';
import { IKosDataModel } from '../kosModel';
import { KosModelRegistrationType } from '../registration/model-registration';
import { KosExecutionContext } from './kos-execution-context';
/**
 * @kosService core.service-request
 */
export type RequestOptions = Omit<KosFetchOptions, "method" | "body">;
export type { KosExecutionContext };
/**
 * Symbol for storing service request handlers metadata on model instances
 * @internal
 */
export declare const ServiceRequestHandlers: unique symbol;
/**
 * HTTP methods supported by kosServiceRequest
 *
 * @kosService core.service-request
 */
export type HttpMethod = "get" | "GET" | "post" | "POST" | "put" | "PUT" | "delete" | "DELETE" | "patch" | "PATCH";
/**
 * API path type - string representing an API endpoint
 *
 * @kosService core.service-request
 */
export type ApiPath = string;
/**
 * Configuration for field mapping in data transformation
 *
 * @kosService core.service-request
 * @kosApiLevel 15
 */
export interface FieldMapping {
    /**
     * Source field path (dot notation supported, e.g., 'data.user.name')
     */
    from: string;
    /**
     * Target field name in the model
     */
    to: string;
    /**
     * Optional transformation to apply
     * Built-in: 'string', 'number', 'boolean', 'date', 'json'
     * Custom: function name or transformer function
     */
    transform?: string | ((value: any) => any);
    /**
     * Whether this field is required
     */
    required?: boolean;
    /**
     * Default value if field is missing
     */
    default?: any;
}
/**
 * Configuration for extension points during transformation
 *
 * @kosService core.service-request
 * @kosApiLevel 15
 */
export interface ExtensionConfig {
    /**
     * Type of extension point
     */
    type: "loader" | "data-mapper" | "validator";
    /**
     * Extension identifier (e.g., ExtensionType.IngredientMapper)
     */
    extension: any;
    /**
     * When to execute the extension
     */
    phase?: "before" | "after" | "transform";
}
/**
 * Configuration for error handling in service requests.
 *
 * Provides multiple strategies for handling request failures, enabling soft failures
 * with cached default values and error-first parameter patterns.
 *
 * @remarks
 * All strategies except 'throw' cache defaultValue in the response store, ensuring
 * consistency across model lifecycle. Lifecycle-triggered handlers receive error-first
 * parameters: (error, data), while method-driven requests use $request executor
 * returning tuple: [error, data].
 *
 * @example
 * ```typescript
 * // Soft failure with logging
 * @kosServiceRequest({
 *   path: '/api/data',
 *   errorHandler: {
 *     strategy: 'log',
 *     defaultValue: []
 *   }
 * })
 * async loadData(error: string | null, data: DataType[]): Promise<void> {
 *   if (error) {
 *     this.logger.error(`Load failed: ${error}`);
 *   }
 *   this.processData(data); // Either real data or defaultValue
 * }
 * ```
 *
 * @category Service Request
 * @see {@link KosServiceRequestConfig} for complete decorator configuration
 * @kosService core.service-request
 * @kosApiLevel 15
 */
export interface ErrorHandlerConfig {
    /**
     * How to handle errors:
     *
     * - 'throw': Re-throw error (default) - handler not called, no caching
     *
     * - 'log': Log error, cache defaultValue, and call handler with (error, defaultValue)
     *   Enables soft failure - log the error but continue processing with default data
     *
     * - 'ignore': Cache defaultValue and call handler with (error, defaultValue)
     *   Soft failure without logging - silent recovery
     *
     * - 'default': Same as 'ignore' - cache defaultValue and call handler
     *   (Kept for backward compatibility, behaves identically to 'ignore')
     *
     * All strategies except 'throw' cache defaultValue in response store for consistency.
     *
     * Note: For lifecycle requests, handler receives error-first parameters: (error, data)
     *       For method-driven requests, $request executor returns tuple: [error, data]
     */
    strategy: "throw" | "log" | "ignore" | "default";
    /**
     * Default value to use when an error occurs.
     * Used with 'log', 'ignore', or 'default' strategies.
     * - Stored in response cache for subsequent access
     * - Passed to handler as second parameter: handler(errorMessage, defaultValue)
     */
    defaultValue?: any;
    /**
     * Custom error handler function called when a request fails.
     *
     * When the failure is an HTTP error response, `error` will be a
     * {@link ServiceRequestError} instance with `status`, `statusText`,
     * `responseBody`, and `responseHeaders` properties.
     *
     * For non-HTTP failures (network errors, timeouts), `error` is a
     * plain `Error`.
     *
     * @example
     * ```typescript
     * onError: (error, model) => {
     *   if (error instanceof ServiceRequestError) {
     *     console.error(`HTTP ${error.status}`, error.responseBody, error.responseHeaders);
     *   }
     * }
     * ```
     */
    onError?: (error: Error | ServiceRequestError, model: IKosDataModel) => void;
}
/**
 * Configuration parameters for @kosServiceRequest decorator
 * Enables lifecycle-driven HTTP requests with declarative data transformation
 *
 * @typeparam Paths - The OpenAPI paths type (from your OpenAPI schema)
 * @typeparam Path - The specific API path being requested
 * @typeparam Method - The HTTP method type
 * @typeparam Response - The raw response type from the API
 * @typeparam TransformedResponse - The transformed response type after mappings
 *
 * @kosService core.service-request
 * @kosApiLevel 15
 */
export interface IKosServiceRequestParams<Paths extends Record<string, any> = Record<string, any>, Path extends keyof Paths = string, Method extends HttpMethod = "get", Response = any, TransformedResponse = Response> {
    /**
     * API path to request
     *
     * @example
     * path: '/api/kos/ingredients'
     * path: `/api/devices/${PROP_DEVICE_ID}/status` // with PropKey
     */
    path: Path;
    /**
     * HTTP method
     *
     * @default 'get'
     */
    method?: Method;
    /**
     * When to execute this request during model lifecycle
     *
     * If omitted, the request executes when the decorated method is called (method-driven).
     * If specified, the request executes automatically during the given lifecycle phase (lifecycle-driven).
     *
     * @example
     * // Lifecycle-driven (auto-execute during LOAD phase)
     * lifecycle: DependencyLifecycle.LOAD
     *
     * @example
     * // Method-driven (execute when method is called)
     * lifecycle: undefined  // or omit entirely
     */
    lifecycle?: DependencyLifecycle;
    /**
     * Path parameters to expand in the URL
     * Strongly typed from OpenAPI schema
     * Can be static values or PropKey references
     *
     * @example
     * // Static value
     * pathParams: { deviceId: 'device-123' }
     *
     * @example
     * // PropKey reference (use PROP_ prefix)
     * pathParams: { deviceId: PROP_DEVICE_ID }
     *
     * @example
     * // Dynamic from method arguments
     * pathParams: (model, deviceId, status) => ({ deviceId })
     */
    pathParams?: (ClientParams<Paths, Path, Method> extends {
        path?: infer P;
    } ? P : never) | ((model: IKosDataModel, ...args: any[]) => ClientParams<Paths, Path, Method> extends {
        path?: infer P;
    } ? P : never);
    /**
     * Query parameters
     * Strongly typed from OpenAPI schema
     *
     * @example
     * // Static
     * queryParams: { includeMetadata: true, format: 'json' }
     *
     * @example
     * // Dynamic from method arguments
     * queryParams: (model, deviceId, debug) => ({ debug })
     */
    queryParams?: (ClientParams<Paths, Path, Method> extends {
        query?: infer Q;
    } ? Q : never) | ((model: IKosDataModel, ...args: any[]) => ClientParams<Paths, Path, Method> extends {
        query?: infer Q;
    } ? Q : never);
    /**
     * Request body (for POST/PUT/PATCH)
     * Strongly typed from OpenAPI schema
     *
     * @example
     * // Static body
     * body: { action: 'start' }
     *
     * @example
     * // Dynamic from method arguments
     * body: (model, vehicleType, speed) => ({ vehicleType, speed })
     */
    body?: ClientBody<Paths, Path, Method> | ((model: IKosDataModel, ...args: any[]) => ClientBody<Paths, Path, Method>);
    /**
     * JSON path to iterate over in response
     * Creates models for each item in the array
     *
     * @example
     * iterateOver: 'data.ingredients' // Iterates response.data.ingredients array
     */
    iterateOver?: string;
    /**
     * Model registration object for creating instances from array items
     * Used with iterateOver to create child models using the builder pattern
     *
     * @example
     * modelFactory: IngredientModel  // The registration object itself
     *
     * deprecated The old curried function signature is deprecated:
     * modelFactory?: (id: string) => (options: any) => IKosDataModel
     */
    modelFactory?: KosModelRegistrationType<IKosDataModel, Record<string, unknown>> | ((id: string) => (options: unknown) => IKosDataModel);
    /**
     * Function to extract the model ID from response data items
     * Required when using modelFactory if items don't have an 'id' field
     *
     * @example
     * idExtractor: (item) => item.ingredientId
     * idExtractor: (item) => `${item.type}-${item.index}`
     */
    idExtractor?: (item: any) => string;
    /**
     * Field mappings for data transformation
     * Same format as @kosTopicHandler mappings
     *
     * @example
     * mappings: [
     *   { from: 'id', to: 'id' },
     *   { from: 'ingredientId', to: 'ingredientId' },
     *   { from: 'metadata.calories', to: 'calories', transform: 'number' }
     * ]
     */
    mappings?: FieldMapping[];
    /**
     * Extension points to execute during transformation
     *
     * @example
     * extensions: [
     *   { type: 'data-mapper', extension: ExtensionType.IngredientMapper }
     * ]
     */
    extensions?: ExtensionConfig[];
    /**
     * Transformation function for response data
     * Applied before mappings
     *
     * @example
     * transform: (response) => response.data
     */
    transform?: (response: Response) => TransformedResponse;
    /**
     * Condition to determine if request should execute
     *
     * @example
     * condition: (model) => model.isInitialized
     */
    condition?: (model: IKosDataModel) => boolean;
    /**
     * Error handling strategy
     *
     * @example
     * errorHandler: {
     *   strategy: 'default',
     *   defaultValue: [],
     *   onError: (error, model) => model.logger.error('Failed to load', error)
     * }
     */
    errorHandler?: ErrorHandlerConfig;
    /**
     * Response caching configuration
     * Controls how long responses are stored and when they're cleaned up
     *
     * @default { retention: ResponseRetention.SINGLE }
     *
     * @example
     * // No caching - clear immediately after handler
     * cache: { retention: ResponseRetention.IMMEDIATE }
     *
     * @example
     * // Cache for 5 minutes
     * cache: { retention: ResponseRetention.TTL, ttl: 5 * 60 * 1000 }
     *
     * @example
     * // Keep for model lifetime
     * cache: { retention: ResponseRetention.PERMANENT }
     */
    cache?: ResponseCacheConfig;
    /**
     * Request options for controlling fetch behavior
     * These are merged with runtime options passed to ctx.$request()
     * Runtime options take precedence over decorator options
     *
     * @example
     * // Set default timeout and destination for all requests
     * requestOptions: {
     *   destinationAddress: 'device-123',
     *   timeout: 10000
     * }
     *
     * @example
     * // Dynamic from method arguments
     * requestOptions: (model, ...args) => ({
     *   destinationAddress: model.deviceId,
     *   headers: { 'X-Session': model.sessionId }
     * })
     * @kosApiLevel 18
     */
    requestOptions?: RequestOptions | ((model: IKosDataModel, ...args: any[]) => RequestOptions);
}
/**
 * Metadata stored for each service request handler
 * @internal
 */
export interface ServiceRequestMetadata<Paths extends Record<string, any> = Record<string, any>, Path extends keyof Paths = string, Method extends HttpMethod = "get", Response = any, TransformedResponse = Response> extends IKosServiceRequestParams<Paths, Path, Method, Response, TransformedResponse> {
    /**
     * The method name that handles the response
     */
    handlerMethod: string;
    /**
     * The original handler function
     */
    originalHandler: Function;
}
/**
 * Runtime service request executor injected into method context
 * Allows imperative execution of the service request defined by the decorator
 *
 * @kosService core.service-request
 */
export interface ServiceRequestExecutor<Paths extends Record<string, any> = Record<string, any>, Path extends keyof Paths = string, Method extends HttpMethod = "get"> {
    (options?: {
        pathParams?: ClientParams<Paths, Path, Method> extends {
            path?: infer P;
        } ? P : never;
        queryParams?: ClientParams<Paths, Path, Method> extends {
            query?: infer Q;
        } ? Q : never;
        body?: ClientBody<Paths, Path, Method>;
        requestOptions?: RequestOptions;
    }): Promise<[string | null, any]>;
}
/**
 * Type alias for the service request executor function signature.
 * This executor is automatically passed as the last parameter to methods
 * decorated with @kosServiceRequest (without lifecycle).
 *
 * @example
 * ```typescript
 * type CreateReleaseExecutor = ServiceRequestExecutor<paths, typeof PATH_VM_INSTALL, 'post'>;
 * ```
 *
 * @kosService core.service-request
 */
export type ServiceRequestExecutorFn<Paths extends Record<string, any> = Record<string, any>, Path extends keyof Paths = string, Method extends HttpMethod = "get"> = ServiceRequestExecutor<Paths, Path, Method>;
/**
 * Decorator for lifecycle-driven service requests within KOS models
 *
 * Eliminates service boilerplate by integrating HTTP requests directly into
 * the model lifecycle. Responses are cached in ServiceResponseStore for access
 * via utility functions, avoiding "magic last parameter" patterns.
 *
 * @param params - Configuration for the service request
 * @returns Method decorator
 *
 * @example Basic GET request during LOAD
 * ```typescript
 * @kosServiceRequest({
 *   path: PATH_DEVICE_STATUS,
 *   method: 'get',
 *   lifecycle: DependencyLifecycle.LOAD,
 *   cache: { retention: ResponseRetention.SINGLE }
 * })
 * private onStatusLoaded(): void {
 *   const response = getServiceResponse(this, PATH_DEVICE_STATUS, 'get');
 *   this.status = response?.status;
 * }
 * ```
 *
 * @example Method-driven request with execution context
 * ```typescript
 * @kosServiceRequest({
 *   path: PATH_VM_INSTALL,
 *   method: 'post',
 *   requestOptions: {
 *     timeout: 10000  // Decorator-level default timeout
 *   }
 * })
 * async createRelease(
 *   path: string,
 *   keysetId?: string,
 *   ctx: KosExecutionContext<paths, typeof PATH_VM_INSTALL, 'post'>
 * ): Promise<void> {
 *   const [error, response] = await ctx.$request({
 *     queryParams: { keySetId: keysetId },
 *     body: { path },
 *     requestOptions: {
 *       destinationAddress: 'device-123',  // Runtime options merged with decorator options
 *       headers: { 'X-Custom-Header': 'value' }
 *     }
 *   });
 *   if (error) throw new Error(error);
 *   return response?.data;
 * }
 * ```
 *
 * @example Container model with iteration
 * ```typescript
 * @kosServiceRequest({
 *   path: PATH_INGREDIENTS,
 *   method: 'get',
 *   lifecycle: DependencyLifecycle.LOAD,
 *   iterateOver: 'data.ingredients',
 *   modelFactory: Ingredient.factory,
 *   mappings: [
 *     { from: 'id', to: 'id' },
 *     { from: 'name', to: 'name' }
 *   ]
 * })
 * private onIngredientsLoaded(): void {
 *   const response = getServiceResponse(this, PATH_INGREDIENTS, 'get');
 *   // Mappings already applied to response data
 *   response?.forEach(i => this.ingredients.addModel(i));
 * }
 * ```
 *
 * @category KOS Model Decorator
 * @kosService core.service-request
 * @kosApiLevel 15
 */
export declare function kosServiceRequest<Paths extends Record<string, any> = Record<string, any>, Path extends keyof Paths = string, Method extends HttpMethod = "get", Response = any, TransformedResponse = Response>(params: IKosServiceRequestParams<Paths, Path, Method, Response, TransformedResponse>): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
//# sourceMappingURL=kos-service-request.d.ts.map