import { ManagerOptions, SocketOptions, Socket } from 'socket.io-client';
import EventEmitter from 'events';

type ClientStatus = {
    readyClients: number;
    runningClients: number;
    finishedClients: number;
};
interface ILogger {
    log: (message: string, type?: string) => void;
    error: (message: string | Error, type?: string) => void;
    warn: (message: string, type?: string) => void;
    debug: (message: string, type?: string) => void;
}

declare class StressEventsEmitter extends EventEmitter {
    on<K extends keyof StressEvents>(eventName: K, listener: StressEvents[K]): this;
    emit<K extends keyof StressEvents>(eventName: K, ...args: Parameters<StressEvents[K]>): boolean;
    once<K extends keyof StressEvents>(eventName: K, listener: StressEvents[K]): this;
    off<K extends keyof StressEvents>(eventName: K, listener: StressEvents[K]): this;
}

/**
 * Configuration options for running IO stress tests
 * @interface IOStressOptions
 * @property {string} target - The target URL/endpoint to stress test
 * @property {StressInterfaceOptions} [interfaceOptions] - Optional stress test input and output configuration. Default: defaultTerminalInterface()
 * @property {StressPhase[]} phases - Array of test phases defining the stress test scenarios
 */
interface IOStressOptions {
    target: string;
    interfaceOptions?: StressInterfaceOptions;
    phases: StressPhase[];
}
/**
 * Configuration for a single phase of stress testing
 * @interface StressPhase
 * @property {string} name - Name of the stress test phase
 * @property {number} minClients - Minimum number of concurrent clients to run
 * @property {number} [maxClients] - Optional maximum number of concurrent clients
 * @property {number} [rampDelayRate] - Default: 100
 * @property {StressScenarioInitializer} [scenarioInitializer] - Optional function to initialize the socket connection
 * @property {string} scenarioPath - The js file that exports stress test scenario function to execute
 * @property {number} [scenarioTimeout] - Optional timeout in milliseconds for the scenario
 * @property {StressInterfaceOptions} [interfaceOptions] - Optional interface options that overrides that top level options for this specific phase.
 */
interface StressPhase {
    name: string;
    minClients: number;
    maxClients?: number;
    rampDelayRate?: number;
    scenarioInitializer?: StressScenarioInitializer;
    scenarioPath: string;
    scenarioTimeout?: number;
    interfaceOptions?: StressInterfaceOptions;
}
/**
 * Configuration options for stress test input and output interface
 * @interface StressInterfaceOptions
 * @property {TerminatorFactory} [terminator] - Optional AbortController factory function for terminating the stress test gracefully/forcefully. Default: defaultTerminalTerminator()
 * @property {StressEventsHandler} [eventsHandler] - Optional events updates handler. Default: defaultTerminalEventsHandler()
 * @property {string} [logsDir] - Optional logs directory. Default: process.cwd()
 */
interface StressInterfaceOptions {
    terminator?: TerminatorFactory;
    eventsHandler?: StressEventsHandler;
    logsDir?: string;
}
type TerminatorFactory = () => AbortController;
declare enum TerminationSignal {
    SIG_SOFT = 0,
    SIG_HARD = 1,
    SIG_CLEANUP = 2
}
/**
 * Stress test events
 * @interface StressEvents
 */
interface StressEvents {
    /**
     * Emitted when the overall stress process has started.
     */
    'process-started': () => void | Promise<void>;
    /**
     * Emitted when a new phase begins.
     *
     * @param phaseName - The name of the phase (e.g. "Warmup", "Load").
     */
    'phase-started': (phaseName: string) => void | Promise<void>;
    /**
     * Emitted when a task within a phase is started or updated.
     *
     * @param description - Human-readable description of the task.
     * @param data - Optional task-related metadata.
     */
    'phase-task': (id: Task, description: string, data?: unknown) => void | Promise<void>;
    /**
     * Emitted when a task within a phase finishes.
     *
     * @param description - Updated description string.
     * @param success - Whether the task completed successfully.
     */
    'phase-task-result': (id: Task, description: string, success: boolean) => void | Promise<void>;
    /**
     * Emitted after a phase is completed with aggregated results.
     *
     * @param report - Aggregated stress test report for the phase.
     * @param workersErrors - Map of worker IDs to arrays of errors.
     */
    'phase-result': (report: StressReport, workersErrors: Record<number, any[]>) => void | Promise<void>;
}
declare enum Task {
    INITIALIZERS_BUILD = 0,
    TEST_RUN = 1,
    REPORT_GENERATE = 2
}
/**
 * A function that handles stress test events updates
 * @param {StressEventsEmitter} eventEmitter - event emitter, that emits stress test events
 * @returns {void}
 */
type StressEventsHandler = (eventEmitter: StressEventsEmitter) => void;
/**
 * A function that returns Socket.io client connection configuration options
 * @param {number} clientNumber - The client number (attempt) to initialize
 * @returns {Partial<ManagerOptions & SocketOptions> | Promise<Partial<ManagerOptions & SocketOptions>>} Socket configuration options
 */
type StressScenarioInitializer = (clientNumber: number) => Partial<ManagerOptions & SocketOptions> | Promise<Partial<ManagerOptions & SocketOptions>>;
/**
 * A stress test scenario function that executes socket operations
 * @param {Socket} socket - The initialized Socket.io client connection
 * @param {ILogger} logger - console.log won't work while executing scenario, use logger instead
 * @returns {void | Promise<void>}
 */
type StressScenario = (socket: Socket, logger: ILogger) => void | Promise<void>;
/**
 * A stress test report (array of phases report)
 */
interface StressReport {
    phase: string;
    testDuration: number;
    connections: {
        attempted: number;
        successful: number;
        failed: number;
        averageConnectionTime: number;
        reconnectAttempts: number;
    };
    events: {
        sent: number;
        received: number;
        successful: number;
        failed: number;
        throughput: number;
    };
    latency: {
        average: number;
        min: number;
        max: number;
        p50: number;
        p85: number;
        p95: number;
        p99: number;
    };
    errors: {
        total: number;
        byType: Record<string, number>;
    };
}

declare function defaultTerminalInterface({ softTerminatorSignal, hardTerminatorSignal, reportsDir, logsDir, }?: {
    softTerminatorSignal?: string;
    hardTerminatorSignal?: string;
    reportsDir?: string;
    logsDir?: string;
}): Required<StressInterfaceOptions>;
declare function defaultTerminalTerminator(softTerminatorSignal?: string, hardTerminatorSignal?: string): () => AbortController;
declare function defaultTerminalEventsHandler(reportsDir?: string): StressEventsHandler;

declare class IOStress {
    private readonly options;
    constructor(options: IOStressOptions);
    run(): Promise<void>;
    private testPhase;
    private buildPhaseInitializers;
}

export { type ClientStatus, type ILogger, IOStress, type IOStressOptions, type StressEvents, type StressEventsHandler, type StressInterfaceOptions, type StressPhase, type StressReport, type StressScenario, type StressScenarioInitializer, Task, TerminationSignal, type TerminatorFactory, defaultTerminalEventsHandler, defaultTerminalInterface, defaultTerminalTerminator };
