import * as jwt              from 'jsonwebtoken';
import User                  from '../sharedByClients/models/User';
import LocalConfig           from '../config/LocalConfig';
import LocalConfigKeys       from '../types/LocalConfigKeys';
import axios, { AxiosError } from 'axios';
import HttpManager           from './HttpManager';
import ora                   from 'ora';
import Permissions           from '../types/Permissions';
import ApiRoutes             from '../sharedByClients/types/ApiRoutes';
import DojoBackendManager    from './DojoBackendManager';
import { StatusCodes }       from 'http-status-codes';


class SessionManager {
    private _token: string | null = null;

    public profile: User | undefined = undefined;

    get isLogged(): boolean {
        return this._token !== null;
    }

    get token(): string {
        return this._token || '';
    }

    set token(token: string) {
        this._token = token;

        try {
            const payload = jwt.decode(token);

            if ( payload && typeof payload === 'object' && payload.profile ) {
                this.profile = payload.profile as User;
            }
        } catch ( error ) {
            this.profile = undefined;
        }

        LocalConfig.updateConfig(LocalConfigKeys.API_TOKEN, token);
    }

    async login(user: string, password: string) {
        const spinner: ora.Ora = ora('Logging in').start();
        try {
            this.profile = undefined;

            const response = await axios.post(DojoBackendManager.getApiUrl(ApiRoutes.LOGIN), {
                user    : user,
                password: password
            });

            spinner.succeed('Logged in');
        } catch ( error ) {
            if ( error instanceof AxiosError ) {
                if ( error.response ) {
                    if ( error.response.status === StatusCodes.NOT_FOUND ) {
                        spinner.fail('User not found or password incorrect');
                    } else {
                        spinner.fail(`Login error: ${ error.response.statusText }`);
                    }
                }
            } else {
                spinner.fail(`Login error: ${ error }`);
            }
        }
    }

    logout() {
        this.token = '';
    }

    checkPermissions(verbose: boolean = true, indent: number = 8, checkPermissions: Array<string> | null = []): Permissions {
        const hasPermission = (permissionPredicate: () => boolean, verboseText: string): boolean => {
            let isAllowed: boolean = this.profile !== undefined && permissionPredicate();

            if ( verbose ) {
                const spinner: ora.Ora = ora({
                                                 text  : verboseText,
                                                 indent: indent
                                             }).start();
                isAllowed ? spinner.succeed() : spinner.fail();
            }

            return isAllowed;
        };

        return {
            teachingStaff: checkPermissions && (checkPermissions.length == 0 || checkPermissions.includes('teachingStaff')) ? hasPermission(() => this.profile?.isTeachingStaff ?? false, 'Teaching staff permissions') : false,
            student      : checkPermissions && (checkPermissions.length == 0 || checkPermissions.includes('student')) ? hasPermission(() => true, 'Student permissions') : false
        };
    }


    async testSession(verbose: boolean = true, checkPermissions: Array<string> | null = []): Promise<false | Permissions> {
        if ( verbose ) {
            ora('Checking Dojo session: ').start().info();
        }

        HttpManager.handleCommandErrors = false;

        const spinner: ora.Ora = ora({
                                         text  : `Testing Dojo session`,
                                         indent: 4
                                     });
        if ( verbose ) {
            spinner.start();
        }

        try {
            await axios.get(DojoBackendManager.getApiUrl(ApiRoutes.TEST_SESSION), {});

            if ( verbose ) {
                spinner.succeed(`The session is valid`);
            }
        } catch ( error ) {
            if ( verbose ) {
                spinner.fail(`The session is invalid`);
            }

            return false;
        }

        return this.checkPermissions(verbose, 8, checkPermissions);
    }
}


export default new SessionManager();