import SharedConfig             from '../shared/config/SharedConfig';
import SonarRoute               from '../shared/types/Sonar/SonarRoute';
import axios, { AxiosInstance } from 'axios';
import Config                   from '../config/Config';
import SonarProjectCreation     from '../shared/types/Sonar/SonarProjectCreation';
import https                    from 'https';
import GlobalHelper             from '../helpers/GlobalHelper';
import DojoStatusCode           from '../shared/types/Dojo/DojoStatusCode';
import express                  from 'express';
import GitlabRepository         from '../shared/types/Gitlab/GitlabRepository';


class SonarManager {
    private instance: AxiosInstance = axios.create({
          httpsAgent: new https.Agent({
              rejectUnauthorized: false
          })
    });

    private getApiUrl(route: SonarRoute): string {
        return `${ SharedConfig.sonar.url }${ route }`;
    }

    /**
     * Assign a Gitlab Personal Access Token to a Sonar account (needed for any other request linked to gitlab)
     * @private
     */
    private async setPAT() {
        const formData = new FormData();
        formData.append('almSetting', 'dojo');
        formData.append('pat', Config.gitlab.account.token);

        await this.instance.post(this.getApiUrl(SonarRoute.SET_PAT), formData, {
            headers: {
                Authorization: `Basic ${ btoa(SharedConfig.sonar.token + ":") }`
            }
        });
    }

    private async executePostRequest<T>(url: string, data?: FormData) {
        await this.setPAT(); // Always set PAT to be sure it has been set

        return (await this.instance.post<T>(url, data, {
            headers: {
                Authorization: `Basic ${ btoa(SharedConfig.sonar.token + ":") }`
            }
        })).data;
    }

    private async executeGetRequest<T>(url: string) {
        return (await this.instance.get<T>(url, {
            headers: {
                Authorization: `Basic ${ btoa(SharedConfig.sonar.token + ":") }`
            }
        })).data;
    }

    async createProjectFromGitlab(projectId: number) {
        const formData = new FormData();
        formData.append('almSetting', 'dojo');
        formData.append('gitlabProjectId', projectId.toString());

        return await this.executePostRequest<SonarProjectCreation>(this.getApiUrl(SonarRoute.PROJECT_CREATE_GITLAB), formData)
    }

    async addQualityGate(projectKey: string, qualityGate: string) {
        const formData = new FormData();
        formData.append('projectKey', projectKey);
        formData.append('gateName', qualityGate);

        return await this.executePostRequest<undefined>(this.getApiUrl(SonarRoute.PROJECT_ADD_GATE), formData);
    }

    async addQualityProfile(projectKey: string, qualityProfile: string, language: string) {
        const formData = new FormData();
        formData.append('project', projectKey);
        formData.append('qualityProfile', qualityProfile);
        formData.append('language', language);

        return await this.executePostRequest<unknown>(this.getApiUrl(SonarRoute.PROJECT_ADD_PROFILE), formData);
    }

    async createProjectWithQualities(gitlabRepository: GitlabRepository, qualityGate: string | null, qualityProfiles: string[] | null, req: express.Request, res: express.Response) {
        let sonarProject: SonarProjectCreation | undefined = undefined;
        try {
            sonarProject = await this.createProjectFromGitlab(gitlabRepository.id);
            if (sonarProject == undefined) {
                return await GlobalHelper.repositoryCreationError('Sonar error', undefined, req, res, DojoStatusCode.ASSIGNMENT_CREATION_SONAR_ERROR, DojoStatusCode.ASSIGNMENT_CREATION_SONAR_ERROR, gitlabRepository);
            }
        } catch ( error ) {
            return await GlobalHelper.repositoryCreationError('Sonar project creation error', error, req, res, DojoStatusCode.ASSIGNMENT_CREATION_SONAR_ERROR, DojoStatusCode.ASSIGNMENT_CREATION_INTERNAL_ERROR, gitlabRepository);
        }
        // Add gate and profiles to sonar project
        if ( qualityGate != undefined && qualityGate != "" ) {
            try {
                await this.addQualityGate(sonarProject.project.key, qualityGate);
            } catch ( error ) {
                return await GlobalHelper.repositoryCreationError('Sonar gate error', error, req, res, DojoStatusCode.ASSIGNMENT_SONAR_GATE_NOT_FOUND, DojoStatusCode.ASSIGNMENT_SONAR_GATE_NOT_FOUND, gitlabRepository);
            }
        }

        if ( qualityProfiles != undefined && qualityProfiles.length > 0 ) {
            for ( const profile of qualityProfiles ) {
                try {
                    const [ lang, name ] = profile.split('/');
                    if (lang.trim() != '' && name.trim() != '') {
                        await this.addQualityProfile(sonarProject.project.key, name.trim(), lang.trim());
                    } else {
                        return await GlobalHelper.repositoryCreationError('Sonar profile invalid', undefined, req, res, DojoStatusCode.ASSIGNMENT_SONAR_PROFILE_NOT_FOUND, DojoStatusCode.ASSIGNMENT_SONAR_PROFILE_NOT_FOUND, gitlabRepository);
                    }
                } catch ( error ) {
                    return await GlobalHelper.repositoryCreationError('Sonar profile not found', error, req, res, DojoStatusCode.ASSIGNMENT_SONAR_PROFILE_NOT_FOUND, DojoStatusCode.ASSIGNMENT_SONAR_PROFILE_NOT_FOUND, gitlabRepository);
                }
            }
        }

        return sonarProject;
    }

    async getLanguages() {
        const resp = await this.executeGetRequest<{ languages: { key: string, name: string }[]}>(this.getApiUrl(SonarRoute.GET_LANGUAGES))
        return resp.languages.map(l => l.key)
    }
}

export default new SonarManager();