import { Context } from 'egg';
import * as OAuth2Server from 'oauth2-server';
import {
  AuthorizationCode, Token,
  AuthorizationCodeModel, ClientCredentialsModel, RefreshTokenModel, PasswordModel, ExtensionModel,
  AuthenticateOptions, AuthorizeOptions, ServerOptions, TokenOptions,
} from 'oauth2-server';
import { SignOptions, SignCallback, VerifyOptions, VerifyCallback } from 'jsonwebtoken';

declare module 'egg' {
  export interface Application {
    oAuth2Server: OAuth2;
    jwt: {
      /**
       * call jsonwebtoken's sign() method
       * @param payload datas. datas to be signed
       * @param secretOrPrivateKey secret key. string or { key, passphrase }
       * @param options jwt options。see more details in https://github.com/auth0/node-jsonwebtoken
       * @param callback callback
       */
      sign(
        payload: string | Buffer | object,
        privateKey: string,
        options?: SignOptions,
        callback?: SignCallback
      ): string;
      /**
       * call jsonwebtoken's verify() method
       * @param token jwt token.
       * @param secretOrPrivateKey secret key。string or { key, passphrase }
       * @param options jwt options。see more details in https://github.com/auth0/node-jsonwebtoken
       * @param callback callback
       */
      verify(token: string, secretOrPrivateKey: string, options?: VerifyOptions, callback?: VerifyCallback): any;

      /**
       * call jsonwebtoken's decode() method
       * @param token jwt token
       */
      decode(token: string): any;

      setPrivateKey(privateKey: string): void;
      setPublicKey(publicKey: string): void;

      privateKey: string | undefined;
      publicKey: string | undefined;
    }
  }
  interface EggAppConfig extends ServerOptions {
    oAuth2Server: {
      jwt: {
        secret: string;
        enable?: boolean;
        sign?: SignOptions;
        verify?: VerifyOptions;
      };
      grants: string[];
      accessTokenLifetime: number;
      refreshTokenLifetime: number;
      requireClientAuthentication: any;
      // whether the token grant response can include extra token attributes, default false
      whiteList: string[];
      // wrap login request http request header content-type to application/json
      loginURL: string;
    }
  }
}

type Model = AuthorizationCodeModel | ClientCredentialsModel | RefreshTokenModel | PasswordModel | ExtensionModel;
type ExecuteOptions = AuthenticateOptions | AuthorizeOptions | TokenOptions;

interface OAuth2ServerConfig extends ServerOptions {
  whiteList: string[];
  loginURL: string;
}
declare class OAuth2 {
  constructor(config: OAuth2ServerConfig, model: Model);
  private config: OAuth2ServerConfig;
  private model: Model;
  private server: OAuth2Server;

  /**
   * Authenticates a request.
   */
  public authenticate(options?: AuthenticateOptions): (ctx: Context, next: Function) => Promise<void>;
  /**
   * Authorizes a token request.
   */
  public authorize(options?: AuthorizeOptions): (ctx: Context, next: Function) => Promise<void>;
  /**
   * Retrieves a new token for an authorized token request.
   */
  public token(options?: TokenOptions): (ctx: Context, next: Function) => Promise<void>;

  public execute(handle: 'authenticate' | 'authorize' | 'token', ctx: Context, options: ExecuteOptions): Promise<AuthorizationCode | Token>;
}
