import LocalConfig     from '../config/LocalConfig';
import LocalConfigKeys from '../types/LocalConfigKeys';
import axios           from 'axios';
import Config          from '../config/Config';
import ora             from 'ora';
import GitlabUser      from '../models/GitlabUser';


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

    constructor() { }

    private static _instance: GitlabManager;

    public static get instance(): GitlabManager {
        if ( !GitlabManager._instance ) {
            GitlabManager._instance = new GitlabManager();
        }

        return GitlabManager._instance;
    }

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

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

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

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

    login(token: string): void {
        this.token = token;
    }

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

    public async testToken(verbose: boolean = true): Promise<[ boolean, boolean ]> {
        if ( verbose ) {
            ora('Checking Gitlab token: ').start().info();
        }

        let result: [ boolean, boolean ] = [ false, false ];

        type NotificationSettings = { level: string }

        let notificationSettings: NotificationSettings = { level: 'error' };

        // Test read access
        {
            const spinnerRead: ora.Ora = ora({
                                                 text  : `Read access`,
                                                 indent: 4
                                             });
            if ( verbose ) {
                spinnerRead.start();
            }

            try {
                notificationSettings = (await this.getNotificationSettings()).data as NotificationSettings;

                result[0] = true;

                if ( verbose ) {
                    spinnerRead.succeed();
                }
            } catch ( e ) {
                if ( verbose ) {
                    spinnerRead.fail();
                }
            }
        }

        // Test write access
        {
            const spinnerWrite: ora.Ora = ora({
                                                  text  : `Write access`,
                                                  indent: 4
                                              });
            if ( verbose ) {
                spinnerWrite.start();
            }

            const someLevelTypes = [ 'disabled', 'participating' ];

            try {
                const oldSettings = notificationSettings;
                const newSettings = { level: someLevelTypes[someLevelTypes[0] == oldSettings.level ? 1 : 0] };

                await this.setNotificationSettings(newSettings);
                await this.setNotificationSettings(oldSettings);

                result[1] = true;

                if ( verbose ) {
                    spinnerWrite.succeed();
                }
            } catch ( e ) {
                if ( verbose ) {
                    spinnerWrite.fail();
                }
            }
        }

        return result;
    }

    public getNotificationSettings() {
        return axios.get(`${ Config.gitlab.apiURL }/notification_settings`);
    }

    public setNotificationSettings(newSettings: any) {
        return axios.put(`${ Config.gitlab.apiURL }/notification_settings`, { params: new URLSearchParams(newSettings) });
    }

    private async getGitlabUser(paramsToSearch: Array<string | number>, paramName: string, verbose: boolean = false, verboseIndent: number = 0): Promise<Array<GitlabUser | undefined>> {
        try {
            return await Promise.all(paramsToSearch.map(async param => {
                const spinner: ora.Ora = ora({
                                                 text  : `Getting user ${ param }`,
                                                 indent: verboseIndent
                                             });
                if ( verbose ) {
                    spinner.start();
                }
                const params: any = {};
                params[paramName] = param;
                const user = await axios.get(`${ Config.gitlab.apiURL }/users`, { params: params });

                if ( user.data[0] ) {
                    const gitlabUser = GitlabUser.createFromJson(user.data[0]);

                    if ( verbose ) {
                        spinner.succeed(`${ gitlabUser.username } (${ gitlabUser.id })`);
                    }
                    return gitlabUser;
                } else {
                    if ( verbose ) {
                        spinner.fail(`${ param }`);
                    }
                }
            }));
        } catch ( e ) {
            return [ undefined ];
        }
    }

    public async getUsersById(ids: Array<number>, verbose: boolean = false, verboseIndent: number = 0): Promise<Array<GitlabUser | undefined>> {
        return await this.getGitlabUser(ids, 'id', verbose, verboseIndent);
    }

    public async getUsersByUsername(usernames: Array<string>, verbose: boolean = false, verboseIndent: number = 0): Promise<Array<GitlabUser | undefined>> {
        return await this.getGitlabUser(usernames, 'search', verbose, verboseIndent);
    }
}


export default GitlabManager.instance;