import type {RequestHandler} from 'express'

/**
 * Asserts that the given request is valid.
 * Throws an error if the request is invalid.
 *
 * @param request - The Connect/Express-like request to verify
 * @param secret - The secret to use for verifying the signature
 * @public
 */
export declare function assertValidRequest(
  request: ConnectLikeRequest,
  secret: string,
): Promise<void>

/**
 * Asserts that the given signature is valid.
 * Throws an error if the signature is invalid.
 *
 * @param stringifiedPayload - The stringified payload to verify - should be straight from the request, not a re-encoded JSON string, as this in certain cases will yield mismatches due to inconsistent encoding.
 * @param signature - The signature to verify against
 * @param secret - The secret to use for verifying the signature
 * @public
 */
export declare function assertValidSignature(
  stringifiedPayload: string,
  signature: string,
  secret: string,
): Promise<void>

/**
 * A Connect/Express-like request object, containing a `headers` object and a `body` property.
 *
 * @public
 */
export declare interface ConnectLikeRequest<B = unknown> {
  headers: Record<string, string | string[] | undefined>
  body: B
}

/**
 * A decoded signature header
 *
 * @public
 */
export declare interface DecodedSignature {
  /**
   * The timestamp the signature was created
   */
  timestamp: number
  /**
   * The hashed payload (base64url encoded)
   */
  hashedPayload: string
}

/**
 * Decode a signature header into a timestamp and hashed payload.
 *
 * @param signaturePayload - The signature header to decode
 * @returns An object with the decoded timestamp and hashed payload
 * @public
 */
export declare function decodeSignatureHeader(signaturePayload: string): DecodedSignature

/**
 * Encodes a signature header for the given payload and timestamp.
 *
 * @param stringifiedPayload - The stringified payload to verify - should be straight from the request, not a re-encoded JSON string, as this in certain cases will yield mismatches due to inconsistent encoding.
 * @param timestamp - The timestamp to use for the signature
 * @param secret - The secret to use for verifying the signature
 * @returns A promise that resolves to the encoded signature header
 * @public
 */
export declare function encodeSignatureHeader(
  stringifiedPayload: string,
  timestamp: number,
  secret: string,
): Promise<string>

/**
 * Checks whether or not the given error is a signature error.
 *
 * @param error - The error to check.
 * @returns `true` if the error is a signature error, otherwise `false`.
 * @public
 */
export declare function isSignatureError(error: unknown): error is WebhookSignatureError

/**
 * Checks if the given request is valid.
 *
 * @param request - The Connect/Express-like request to verify
 * @param secret - The secret to use for verifying the signature
 * @returns Promise that resolves to `true` if the request is valid, `false` otherwise.
 * @public
 */
export declare function isValidRequest(
  request: ConnectLikeRequest,
  secret: string,
): Promise<boolean>

/**
 * Checks if the given signature is valid.
 *
 * @param stringifiedPayload - The stringified payload to verify - should be straight from the request, not a re-encoded JSON string, as this in certain cases will yield mismatches due to inconsistent encoding.
 * @param signature - The signature to verify against
 * @param secret - The secret to use for verifying the signature
 * @returns A promise that resolves to `true` if the signature is valid, `false` otherwise.
 * @public
 */
export declare function isValidSignature(
  stringifiedPayload: string,
  signature: string,
  secret: string,
): Promise<boolean>

/**
 * Express/Connect style middleware that verifies the signature of a request.
 * Should be added _after_ a body parser that parses the request body to _text_, not parsed JSON.
 *
 * @example
 * ```ts
 * import express from 'express'
 * import bodyParser from 'body-parser'
 * import {requireSignedRequest} from '@sanity/webhook'
 *
 * express()
 *   .use(bodyParser.text({type: 'application/json'}))
 *   .post(
 *     '/hook',
 *     requireSignedRequest({secret: process.env.MY_WEBHOOK_SECRET, parseBody: true}),
 *     function myRequestHandler(req, res) {
 *       // Note that `req.body` is now a parsed version, set `parseBody` to `false`
 *       // if you want the raw text version of the request body
 *     },
 *   )
 *   .listen(1337)
 * ```
 *
 * @param options - Options for the middleware
 * @returns A middleware function
 * @public
 */
export declare function requireSignedRequest(options: SignatureMiddlewareOptions): RequestHandler

/**
 * The name of the header that contains the signature.
 *
 * @public
 */
export declare const SIGNATURE_HEADER_NAME = 'sanity-webhook-signature'

/**
 * Options for the `requireSignedRequest` middleware
 *
 * @public
 */
export declare interface SignatureMiddlewareOptions {
  /**
   * The secret to use for verifying the signature
   */
  secret: string
  /**
   * Whether or not to parse the request body as JSON on success (assigns it to `request.body`).
   * Default: `true`
   */
  parseBody?: boolean
  /**
   * Whether or not to respond with an error when the signature is invalid.
   * If `false`, it will call the `next` function with the error instead.
   * Default: `true`
   */
  respondOnError?: boolean
}

/**
 * Error types used on signature errors.
 * Includes `type` and `statusCode` properties.
 *
 * @public
 */
export declare type WebhookSignatureError = WebhookSignatureValueError | WebhookSignatureFormatError

/**
 * Error thrown when the signature format is invalid.
 * This can happen when the signature is not a string or is not in the format of `t=<timestamp>,v=<signature>`.
 * This error is also thrown when the timestamp is not a number or is not within the tolerance time.
 *
 * @public
 */
export declare class WebhookSignatureFormatError extends Error {
  type: string
  statusCode: number
}

/**
 * Error thrown when the signature value does not match the expected value.
 *
 * @public
 */
export declare class WebhookSignatureValueError extends Error {
  type: string
  statusCode: number
}

export {}
