import type { Server } from 'node:http';
import type { REPLServer } from 'node:repl';
import type { BaseLogger, Logger } from 'pino';
import type { Request, Response } from 'express';
import type { Application } from 'express-serve-static-core';
import type { middleware } from 'express-openapi-validator';
import type { Meter } from '@opentelemetry/api';
import type { NodeSDKConfiguration } from '@opentelemetry/sdk-node';
import type { ShortstopHandler } from '@sesamecare-oss/confit';
import type { ConfigurationSchema } from './config/schema.js';
export interface InternalLocals<SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>> extends Record<string, unknown> {
    server?: Server;
    mainApp: ServiceExpress<SLocals>;
}
export type ServiceLogger = BaseLogger & Pick<Logger, 'isLevelEnabled'>;
export interface ServiceLocals<Config extends ConfigurationSchema = ConfigurationSchema> {
    service: Service;
    name: string;
    version: string;
    logger: ServiceLogger;
    config: Config;
    meter: Meter;
    internalApp: Application<InternalLocals<this>>;
    /**
     * This is the parsed OpenAPI spec we are hosting (if openapi is enabled)
     */
    openApiSpecification?: ReturnType<typeof JSON.parse>;
}
export interface RequestLocals {
    rawBody?: Buffer | true;
    logger: ServiceLogger;
}
export type ServiceExpress<Locals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>> = Application<Locals>;
export type RequestWithApp<Locals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>> = Omit<Request, 'app'> & {
    app: Application<Locals>;
};
export type ResponseFromApp<ResBody = unknown, RLocals extends RequestLocals = RequestLocals> = Response<ResBody, RLocals>;
/**
 * This is the core type you need to implement to provide a service
 */
export interface Service<SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>, RLocals extends RequestLocals = RequestLocals> {
    name?: string;
    configure?: (startOptions: ServiceStartOptions<SLocals, RLocals>, options: ServiceOptions) => ServiceOptions;
    attach?: (app: ServiceExpress<SLocals>) => void | Promise<void>;
    attachServer?: (app: ServiceExpress<SLocals>, server: Server) => void | Promise<void>;
    onListening?: (app: ServiceExpress<SLocals>, info: {
        port?: number;
        protocol: 'http' | 'https';
    }) => void | Promise<void>;
    start(app: ServiceExpress<SLocals>): void | Promise<void>;
    stop?: (app: ServiceExpress<SLocals>) => void | Promise<void>;
    healthy?: (app: ServiceExpress<SLocals>) => boolean | Promise<boolean>;
    onRequest?(req: RequestWithApp<SLocals>, res: Response<unknown, RLocals>): void | Promise<void>;
    authorize?(req: RequestWithApp<SLocals>, res: Response<unknown, RLocals>): boolean | Promise<boolean>;
    getLogFields?(req: RequestWithApp<SLocals>, values: Record<string, string | string[] | number | undefined>): string | undefined;
    attachRepl?(app: ServiceExpress<SLocals>, repl: REPLServer): void;
}
export type ServiceFactory<SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>, RLocals extends RequestLocals = RequestLocals> = () => Service<SLocals, RLocals>;
export interface ServiceStartOptions<SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>, RLocals extends RequestLocals = RequestLocals> {
    name: string;
    version: string;
    rootDirectory: string;
    codepath?: 'build' | 'src' | 'dist';
    locals?: Partial<SLocals>;
    service: () => Service<SLocals, RLocals>;
}
export interface DelayLoadServiceStartOptions extends Omit<ServiceStartOptions, 'service'> {
    service: string;
    customizer?: ((options: Partial<NodeSDKConfiguration>) => Partial<NodeSDKConfiguration>) | undefined;
}
export interface ServiceOptions {
    codepath?: string;
    codeExtension?: string;
    configurationDirectories: string[];
    openApiOptions?: Partial<Parameters<typeof middleware>[0]>;
    shortstopHandlers: Record<string, ShortstopHandler<string, unknown>>;
}
export interface ServiceLike<SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>> {
    locals: SLocals;
}
/**
 * This type should be used (or extended) to pass "context"
 * into functions not directly wired into the Express request
 * handling flow. It will allow "synthetic" requests to be
 * easily constructed without depending on things they should not,
 * like query strings or body or similar. Most often, you want the
 * logger.
 */
export interface RequestLike<SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>, RLocals extends RequestLocals = RequestLocals> {
    app: ServiceLike<SLocals>;
    res: {
        locals: RLocals;
    };
}
export interface ServiceTypes<SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>, RLocals extends RequestLocals = RequestLocals, ResBody = unknown> {
    App: ServiceExpress<SLocals>;
    Handler: (req: RequestWithApp<SLocals>, res: ResponseFromApp<ResBody, RLocals>) => void | Promise<void>;
    Request: RequestWithApp<SLocals>;
    RequestLike: RequestLike<SLocals, RLocals>;
    RequestLocals: RLocals;
    Response: ResponseFromApp<ResBody, RLocals>;
    Service: Service<SLocals, RLocals>;
    ServiceFactory: ServiceFactory<SLocals, RLocals>;
    ServiceLocals: SLocals;
}
export type UnwrapServiceConfig<SLocals extends ServiceLocals> = SLocals extends ServiceLocals<infer C> ? C : never;
export type AnyServiceLocals = ServiceLocals<any>;
