/* eslint-disable @typescript-eslint/no-namespace */
import type { TRPCRequestInfo } from '../http/types';
import type { ProcedureType } from '../procedure';
import type { TRPC_ERROR_CODE_NUMBER } from './codes';

/**
 * Error response
 */
export interface TRPCErrorShape<TData extends object = object> {
  code: TRPC_ERROR_CODE_NUMBER;
  message: string;
  data: TData;
}

/**
 * JSON-RPC 2.0 Specification
 */
export namespace JSONRPC2 {
  export type RequestId = number | string | null;

  /**
   * All requests/responses extends this shape
   */
  export interface BaseEnvelope {
    id?: RequestId;
    jsonrpc?: '2.0';
  }

  export interface BaseRequest<TMethod extends string = string>
    extends BaseEnvelope {
    method: TMethod;
  }

  export interface Request<TMethod extends string = string, TParams = unknown>
    extends BaseRequest<TMethod> {
    params: TParams;
  }

  export interface ResultResponse<TResult = unknown> extends BaseEnvelope {
    result: TResult;
  }

  export interface ErrorResponse<TError extends TRPCErrorShape = TRPCErrorShape>
    extends BaseEnvelope {
    error: TError;
  }
}

/////////////////////////// HTTP envelopes ///////////////////////

export interface TRPCRequest
  extends JSONRPC2.Request<
    ProcedureType,
    {
      path: string;
      input: unknown;
      /**
       * The last event id that the client received
       */
      lastEventId?: string;
    }
  > {}

export interface TRPCResult<TData = unknown> {
  data: TData;
  type?: 'data';
  /**
   * The id of the message to keep track of in case of a reconnect
   */
  id?: string;
}

export interface TRPCSuccessResponse<TData>
  extends JSONRPC2.ResultResponse<TRPCResult<TData>> {}

export interface TRPCErrorResponse<
  TError extends TRPCErrorShape = TRPCErrorShape,
> extends JSONRPC2.ErrorResponse<TError> {}

export type TRPCResponse<
  TData = unknown,
  TError extends TRPCErrorShape = TRPCErrorShape,
> = TRPCErrorResponse<TError> | TRPCSuccessResponse<TData>;

/////////////////////////// WebSocket envelopes ///////////////////////

export type TRPCRequestMessage = TRPCRequest & {
  id: JSONRPC2.RequestId;
};

/**
 * The client asked the server to unsubscribe
 */
export interface TRPCSubscriptionStopNotification
  extends JSONRPC2.BaseRequest<'subscription.stop'> {
  id: null;
}

/**
 * The client's outgoing request types
 */
export type TRPCClientOutgoingRequest = TRPCSubscriptionStopNotification;

/**
 * The client's sent messages shape
 */
export type TRPCClientOutgoingMessage =
  | TRPCRequestMessage
  | (JSONRPC2.BaseRequest<'subscription.stop'> & { id: JSONRPC2.RequestId });

export interface TRPCResultMessage<TData>
  extends JSONRPC2.ResultResponse<
    | { type: 'started'; data?: never }
    | { type: 'stopped'; data?: never }
    | TRPCResult<TData>
  > {}

export type TRPCResponseMessage<
  TData = unknown,
  TError extends TRPCErrorShape = TRPCErrorShape,
> = { id: JSONRPC2.RequestId } & (
  | TRPCErrorResponse<TError>
  | TRPCResultMessage<TData>
);

/**
 * The server asked the client to reconnect - useful when restarting/redeploying service
 */
export interface TRPCReconnectNotification
  extends JSONRPC2.BaseRequest<'reconnect'> {
  id: JSONRPC2.RequestId;
}

/**
 * The client's incoming request types
 */
export type TRPCClientIncomingRequest = TRPCReconnectNotification;

/**
 * The client's received messages shape
 */
export type TRPCClientIncomingMessage<
  TResult = unknown,
  TError extends TRPCErrorShape = TRPCErrorShape,
> = TRPCClientIncomingRequest | TRPCResponseMessage<TResult, TError>;

/**
 * The client sends connection params - always sent as the first message
 */
export interface TRPCConnectionParamsMessage
  extends JSONRPC2.BaseRequest<'connectionParams'> {
  data: TRPCRequestInfo['connectionParams'];
}
