import type { Boom } from '@hapi/boom';
import { SealOptions, SealOptionsSub } from '@hapi/iron';

export { SealOptions, SealOptionsSub };

export type SameSitePolicy = false | 'None' | 'Lax' | 'Strict';

export interface StateOptions<HapiRequest> {
  /**
   * If `false`, allows any cookie value including values in violation of [RFC 6265](https://tools.ietf.org/html/rfc6265).
   *
   * @default true
   */
  strictHeader?: boolean | undefined;

  /**
   * If `true`, errors are ignored and treated as missing cookies.
   */
  ignoreErrors?: boolean | undefined;

  /**
   * Sets the `Secure` flag.
   *
   * @default true
   */
  isSecure?: boolean | undefined;

  /**
   * Sets the `HttpOnly` flag.
   *
   * @default true
   */
  isHttpOnly?: boolean | undefined;

  /**
   * Sets the `SameSite` flag. The value must be one of:
   *
   *     - `false`  - no flag.
   *     - `Strict` - sets the value to `Strict`.
   *     - `Lax`    - sets the value to `Lax`.
   *     - `None`   - sets the value to `None`.
   *
   *  @default 'Strict'
   */
  isSameSite?: SameSitePolicy | undefined;

  /**
   * Sets the `Partitioned` flag. For more information, please access https://developers.google.com/privacy-sandbox/3pcd/chips
   */
  isPartitioned?: boolean | undefined;

  /**
   * The path scope.
   *
   * @default null (no path)
   */
  path?: string | null | undefined;

  /**
   * The domain scope.
   *
   * @default null (no domain)
   */
  domain?: string | null | undefined;

  /**
   * Time-to-live in milliseconds.
   *
   * @default null (session time-life - cookies are deleted when the browser is closed)
   */
  ttl?: number | null | undefined;

  /**
   * Encoding performs on the provided value before serialization. Options are:
   *
   *     - `none` - no encoding. When used, the cookie value must be a string.
   *     - `base64` - string value is encoded using Base64.
   *     - `base64json` - object value is JSON-stringified then encoded using Base64.
   *     - `form` - object value is encoded using the x-www-form-urlencoded method.
   *     - `iron` - Encrypts and sign the value using iron.
   *
   * @default 'none'
   */
  encoding?: 'none' | 'base64' | 'base64json' | 'form' | 'iron' | undefined;

  /**
   * An object used to calculate an HMAC for cookie integrity validation. This does not provide privacy, only a mean
   * to verify that the cookie value was generated by the server. Redundant when 'iron' encoding is used. Options are:
   *  - integrity -
   *  - password -
   */
  sign?:
    | {
        /**
         * Algorithm options.
         */
        integrity?: SealOptionsSub | undefined;

        /**
         * Password used for HMAC key generation (must be at least 32 characters long).
         */
        password: string;
      }
    | undefined;

  /**
   * Options for 'iron' encoding.
   */
  iron?: SealOptions | undefined;

  /**
   * Password used for 'iron' encoding (must be at least 32 characters long).
   */
  password?: string | undefined;

  /**
   * A function using the signature `async function(definition, request)` used to override a request-specific cookie settings.
   */
  contextualize?(
    /**
     * A copy of the options to be used for formatting the cookie that can be manipulated by the function to customize
     * the request cookie header. Note that changing the `definition.contextualize` property will be ignored.
     */
    definition: this,
    /**
     * The request object.
     */
    request: HapiRequest
  ): void | Promise<void>;

  /**
   * If true, automatically instruct the client to remove invalid cookies.
   *
   * @default false
   */
  clearInvalid?: boolean | undefined;

  /**
   * if present and the cookie was not received from the client or explicitly set by the route handler, the
   * cookie is automatically added to the response with the provided value. The value can be
   * a function with signature async function(request) where:
   */
  autoValue?(request: HapiRequest): any;

  /**
   * Used by proxy plugins (e.g. h2o2).
   */
  passThrough?: any | undefined;
}

export interface FormatCookie<HapiRequest> {
  name: string;
  value: any;
  options: StateOptions<HapiRequest>;
}

export class Definitions<HapiRequest> {
  constructor(options: StateOptions<HapiRequest>);

  add(name: string, options: StateOptions<HapiRequest>): void;

  parse(cookies: string): Promise<{
    states: Record<string, string>;
    failed: {
      name?: string;
      value?: string;
      settings: StateOptions<HapiRequest>;
      reason: string;
    }[];
  }>;

  format(
    cookies: FormatCookie<HapiRequest> | FormatCookie<HapiRequest>[],
    context: HapiRequest
  ): Promise<string[]>;

  passThrough(header: string, fallback: boolean): string | Boom;
}

export function prepareValue(
  name: string,
  value: any,
  options: StateOptions<any>
): Promise<string>;

export function exclude(cookies: string, excludes: string[]): string | Boom;
