/* eslint-disable camelcase */
import * as querystring from "querystring";
import { Option, tryDo, isHttpError } from "@shared/common/async_utils";
import got, { HTTPError } from "got";
import { logger } from "./logging";

const DISCORD_API = "https://discord.com/api";

export interface AccessTokenResponse {
    access_token: string;
    token_type: string;
    expires_in: number;
    refresh_token: string;
    scope: string;
}

type GrantType = "authorization_code";
type TokenType = "code" | "token";

export interface AccessTokenRequest {
    client_id: string;
    client_secret: string;
    grant_type: GrantType;
    code: string;
    scope: string;
    redirect_uri: string;
}

export type AuthorizeRequest = {
    client_id: string,
    redirect_url: string,
    response_type: TokenType,
    scope: string,
}

export interface DiscordUser {
    id: string;
    username: string;
    discriminator: string;
    avatar?: string;
    bot?: boolean;
    mfa_enabled?: boolean;
    locale?: string;
    verified?: boolean;
    email?: string;
    flags?: number;
    premium_type?: number;
}

export class DiscordAPI {
    static getAuthUrl(opts: AuthorizeRequest): string {
        return `${DISCORD_API}/oauth2/authorize?${querystring.stringify(opts)}`;
    }

    static async getToken(opts: AccessTokenRequest):
                        Promise<Option<AccessTokenResponse, { error: string }>> {
        const result = await tryDo(got<AccessTokenResponse>("oauth2/token", {
            responseType: "json",
            method: "post",
            form: opts,
            prefixUrl: DISCORD_API,
        }));

        if (!result.ok) {
            if (isHttpError<HTTPError>(result.error)) {
                logger.warn("WEB: Failed to authenticate a user: %s", result.error);
                return { error: "Couldn't authenticate a user (the session might be old). Please try logging in again.", ok: false };
            }
            logger.error("WEB: failed to fetch access token: %s", result.error);
            return { error: "Unexpected error", ok: false };
        }
        return { ok: true, ...result.result.body };
    }

    static async getCurrentUser(token: string): Promise<Option<DiscordUser, {error: string}>> {
        const result = await tryDo(got<DiscordUser>("users/@me", {
            responseType: "json",
            headers: {
                Authorization: `Bearer ${token}`,
            },
            prefixUrl: DISCORD_API,
        }));

        if (!result.ok) {
            if (isHttpError<HTTPError>(result.error)) {
                return { error: `Failed to check used ID. Error: ${result.error.message}`, ok: false };
            }
            return { error: "Unexpected error", ok: false };
        }
        return { ok: true, ...result.result.body };
    }
}
