import * as jwt from 'jsonwebtoken';
import { Base64 } from 'js-base64';

import * as fs from 'fs';

import { checkPassword } from './generate';
import { db } from './db';
import { UUID } from './global/scalars/UUID';


const SECRET = fs.readFileSync('./assets/keys/jwt.key');
const PUBLIC = fs.readFileSync('./assets/keys/jwt.key.pub');


export class Client {
    private readonly id: UUID;

    private readonly connectTime: number;
    private readonly ip: string;
    private token: string = null;

    private master: boolean = false;
    private adminOf: UUID[] = [];
    private organizerOf: UUID[] = [];

    constructor(id?: UUID, ip?: string) {
        this.id = id;
        this.ip = ip;
        this.connectTime = Date.now();
    }

    public async login(data: {
        email?: string,
        passwort?: string,
        token?: string,
    }): Promise<void> {
        if (data.token) {
            let token = null;
            try {
                token = jwt.verify(data.token, PUBLIC);
            } catch (e) {
                return this.LoginFailed();
            }

            this.token = data.token;
            return this.LoginSuccessful(token._id);
        }

        if (data.email && data.passwort) {
            const entries: any[] = (await Promise.all(
              (await db.fetch('person', { 'email': data.email } ))
                .map(async (e) => ({
                    ...e,
                    authOK: await checkPassword(data.passwort, e.passwort)
                }))))
              .filter(e => e.authOK);
            if (entries.length !== 1) {
                return this.LoginFailed();
            }

            return this.LoginSuccessful(entries[0]._id);
        }

        return this.LoginFailed();
    }

    private async LoginFailed(): Promise<void> {
        this.token = null;
        this.master = false;
    }

    private async LoginSuccessful(userid: string): Promise<void> {
        const entry = (await db.fetch('person', {_id: userid}))?.[0]
        if (!entry) {
            return;
        }

        this.token = jwt.sign({
            vorname: entry.givenName,
            nachname: entry.familyName,
            _id: userid,
            iat: Math.floor(Date.now() / 1000),
        }, SECRET, {
            algorithm: 'RS256',
            expiresIn: 3600*12,
        })

        this.master = entry.master;

        this.adminOf = (await db.fetch('organizer', { _admins: userid })).map(e => e._id);
        this.organizerOf = (await db.fetch('organizer', { _organizers: userid })).map(e => e._id);

        return;
    }

    /*public getID(): UUID {
        return this.id
    }

    public getIP(): string {
        return this.ip
    }*/

    public getConnectTime(): number {
        return this.connectTime;
    }

    public getToken(): string {
        return this.token;
    }

    public getUser(): any {
        if (!this.token) { return null; }
        return JSON.parse(Base64.decode(this.token?.split('.')?.[1] || ''));
    }

    public isSelf(id: UUID): boolean {
        return this.getUser()?._id === id;
    }

    public isMaster(): boolean {
        return this.master;
    }

    public isAdmin(id: UUID): boolean {
        return this.adminOf.indexOf(id) !== -1;
    }

    public isOrganizer(id: UUID): boolean {
        return this.isAdmin(id) || this.organizerOf.indexOf(id) !== -1;
    }
}
