import Err from '@openaddresses/batch-error';
import { Type, Static } from '@sinclair/typebox';
import Commands from '../commands.js';

export const LoginInput = Type.Object({
    username: Type.String(),
    password: Type.String()
})

export const TokenContents = Type.Object({
    sub: Type.String(),
    aud: Type.String(),
    nbf: Type.Number(),
    exp: Type.Number(),
    iat: Type.Number()
})

export default class OAuthCommands extends Commands {
    schema = {}

    async cli(): Promise<object | string> {
        throw new Error('Unsupported Subcommand');
    }

    parse(jwt: string): Static<typeof TokenContents>{
        const split = Buffer.from(jwt, 'base64').toString().split('}').map((ext) => { return ext + '}'});
        if (split.length < 2) throw new Err(500, null, 'Unexpected TAK JWT Format');
        const contents: { sub: string; aud: string; nbf: number; exp: number; iat: number; } = JSON.parse(split[1]);

        return contents;
    }

    async login(query: Static<typeof LoginInput>): Promise<{
        token: string;
        contents: Static<typeof TokenContents>
    }> {
        const url = new URL(`/oauth/token`, this.api.url);

        const params = new URLSearchParams();
        params.append('grant_type', 'password');
        params.append('username', query.username);
        params.append('password', query.password);

        const authres = await this.api.fetch(url, {
            method: 'POST',
            body: params
        }, true);

        const text = await authres.text();

        if ([401, 403].includes(authres.status)) {
            throw new Err(400, new Error(text), 'TAK Server reports incorrect Username or Password');
        } else if (!authres.ok) {
            throw new Err(400, new Error(`Status: ${authres.status}: ${text}`), 'Non-200 Response from Auth Server - Token');
        }

        const body: any = JSON.parse(text);

        if (body.error === 'invalid_grant' && body.error_description.startsWith('Bad credentials')) {
            throw new Err(400, null, 'Invalid Username or Password');
        } else if (body.error || !body.access_token) {
            throw new Err(500, new Error(body.error_description), 'Unknown Login Error');
        }

        return {
            token: body.access_token,
            contents: this.parse(body.access_token)
        };
    }
}
