import { OverwriteWith, AnyFunction } from '@andrew_l/toolkit';

type AnyServiceActor = {
    traceId: any;
    actorId: any;
    actorType: any;
    [x: PropertyKey]: any;
};
type ServiceActorData<T extends Record<PropertyKey, any> = {}> = OverwriteWith<{
    traceId: string;
    actorId: string | null;
    actorType: 'unknown' | string;
}, T>;
type ServiceActor<T extends Record<PropertyKey, any> = {}> = ServiceActorData<T> & {
    assign(params: Partial<ServiceActorData<T>>): ServiceActor<T>;
};
/**
 * Create service actor hooks
 *
 * @example
 * const { use: useServiceActor, with: withServiceActor } = serviceActor(() => ({
 *   actorType: 'http-request',
 *   ipAddress: '',
 *  }));
 *
 * app.use((ctx, next) => {
 *   return withServiceActor(
 *     {
 *       traceId: ctx.headers['x-request-id'],
 *       ipAddress: ctx.headers['x-forwarded-for'],
 *     },
 *     next,
 *   );
 * });
 *
 * app.get('/', () => {
 *   const actor = useServiceActor();
 *
 *   // { traceId: 'req_35123', actorId: null, actorType: 'http-request', ipAddress: '::' }
 *   console.log(actor);
 * });
 *
 * @group Main
 */
declare function serviceActor<T extends Record<PropertyKey, any> = {}>(factory?: () => T): {
    /**
     * Wrap a function to execute it with service actor providers
     */
    with: <Fn extends AnyFunction>(fn: Fn, params?: Partial<ServiceActor<T>>) => Fn;
    /**
     * Returns the service actor instance from the current context.
     */
    inject: () => ServiceActor<T> | undefined;
    /**
     * Returns the service actor instance from the context, or creates and binds a new instance if none exists.
     */
    use: () => ServiceActor<T>;
};

export { type AnyServiceActor, type ServiceActor, type ServiceActorData, serviceActor };
