// Read from the .env file
// ATTENTION : This lines MUST be the first of this file (except for the path import)
const path = require('node:path');
require('dotenv').config({ path: path.join(__dirname, '../.env') });
require('./shared/helpers/TypeScriptExtensions'); // ATTENTION : This line MUST be the second of this file

import ClientsSharedConfig         from './sharedByClients/config/ClientsSharedConfig';
import Styles                      from './types/Style';
import Icon                        from './sharedByClients/types/Icon';
import RecursiveFilesStats         from './shared/helpers/recursiveFilesStats/RecursiveFilesStats';
import Toolbox                     from './shared/helpers/Toolbox';
import ExerciseCheckerError        from './shared/types/Dojo/ExerciseCheckerError';
import { exec }                    from 'child_process';
import util                        from 'util';
import fs                          from 'fs-extra';
import HttpManager                 from './managers/HttpManager';
import DojoBackendManager          from './managers/DojoBackendManager';
import Config                      from './config/Config';
import ArchiveHelper               from './shared/helpers/ArchiveHelper';
import ExerciseDockerCompose       from './sharedByClients/helpers/Dojo/ExerciseDockerCompose';
import ExerciseResultsValidation   from './sharedByClients/helpers/Dojo/ExerciseResultsValidation';
import ExerciseAssignment          from './sharedByClients/models/ExerciseAssignment';
import ClientsSharedExerciseHelper from './sharedByClients/helpers/Dojo/ClientsSharedExerciseHelper';


(async () => {
    const execAsync = util.promisify(exec);

    HttpManager.registerAxiosInterceptor();

    console.log(Styles.APP_NAME(Config.appName));

    let exerciseAssignment: ExerciseAssignment | undefined;
    let exerciseDockerCompose: ExerciseDockerCompose;
    let exerciseResultsValidation: ExerciseResultsValidation;

    /*
     //////////////////////////////////////////////////////////////////////////////////////////////////////////// Step 1:
     -   Read the dojo assignment file from the assignment repository
     -   Download immutables files (maybe throw or show an error if the files have been modified ?)
     */
    {
        console.log(Styles.INFO(`${ Icon.INFO }️ Checking the exercise's assignment and his immutable files`));
        exerciseAssignment = await DojoBackendManager.getExerciseAssignment();
        if ( !exerciseAssignment ) {
            console.error(Styles.ERROR(`${ Icon.ERROR } Error while getting the exercise's assignment`));
            process.exit(ExerciseCheckerError.EXERCISE_ASSIGNMENT_GET_ERROR);
        }

        exerciseAssignment.immutable.forEach(immutableFile => {
            const filePath = path.join(Config.folders.project, immutableFile.file_path);
            fs.mkdirSync(path.dirname(filePath), { recursive: true });
            fs.writeFileSync(filePath, immutableFile.content, { encoding: 'base64' });
        });
    }


    /*
     //////////////////////////////////////////////////////////////////////////////////////////////////////////// Step 2:
     - Get override of docker-compose file (for override the volume by a bind mount to the results folder shared between dind and the host)
     - Run docker-compose file
     - Get logs from linked services
     */
    {
        const composeOverridePath: string = path.join(Config.folders.project, 'docker-compose-override.yml');

        const composeOverride = fs.readFileSync(path.join(__dirname, '../assets/docker-compose-override.yml'), 'utf8').replace('{{VOLUME_NAME}}', exerciseAssignment.assignmentFile.result.volume).replace('{{MOUNT_PATH}}', Config.folders.resultsExercise);
        fs.writeFileSync(composeOverridePath, composeOverride);

        exerciseDockerCompose = new ExerciseDockerCompose(ClientsSharedConfig.dockerCompose.projectName, exerciseAssignment.assignmentFile, Config.folders.project, [ composeOverridePath ]);

        try {
            await new Promise<void>((resolve, reject) => {
                exerciseDockerCompose.events.on('step', (name: string, message: string) => {
                    console.log(Styles.INFO(`${ Icon.INFO } ${ message }`));
                });

                exerciseDockerCompose.events.on('endStep', (stepName: string, message: string, error: boolean) => {
                    if ( error ) {
                        console.error(Styles.ERROR(`${ Icon.ERROR } ${ message }`));
                    }
                });

                exerciseDockerCompose.events.on('finished', (success: boolean, exitCode: number) => {
                    success ? resolve() : reject();
                });

                exerciseDockerCompose.run();
            });
        } catch ( error ) { }

        fs.rmSync(composeOverridePath);
        fs.writeFileSync(path.join(Config.folders.resultsDojo, 'dockerComposeLogs.txt'), exerciseDockerCompose.allLogs);

        if ( !exerciseDockerCompose.success ) {
            console.error(Styles.ERROR(`${ Icon.ERROR } Execution logs are available in artifacts`));
            process.exit(exerciseDockerCompose.exitCode);
        }
    }


    //////////////////////////////////////////////////////////////////////////////////////////////////////////// Step 3: Check content requirements and content size
    {
        exerciseResultsValidation = new ExerciseResultsValidation(Config.folders.resultsDojo, Config.folders.resultsExercise);

        try {
            await new Promise<void>((resolve) => {
                exerciseResultsValidation.events.on('step', (name: string, message: string) => {
                    console.log(Styles.INFO(`${ Icon.INFO } ${ message }`));
                });

                exerciseResultsValidation.events.on('endStep', (stepName: string, message: string, error: boolean) => {
                    if ( error ) {
                        console.error(Styles.ERROR(`${ Icon.ERROR } ${ message }`));
                    }
                });

                exerciseResultsValidation.events.on('finished', (success: boolean, exitCode: number) => {
                    if ( !success ) {
                        process.exit(exitCode);
                    }

                    resolve();
                });

                exerciseResultsValidation.run();
            });
        } catch ( error ) { }
    }


    //////////////////////////////////////////////////////////////////////////////////////////////////////////// Step 4: Upload results
    {
        try {
            console.log(Styles.INFO(`${ Icon.INFO } Uploading results to the dojo server`));
            const commit: any = {};
            Toolbox.getKeysWithPrefix(process.env, 'CI_COMMIT_').forEach(key => {
                commit[Toolbox.snakeToCamel(key.replace('CI_COMMIT_', ''))] = process.env[key];
            });

            const files = await RecursiveFilesStats.explore(Config.folders.resultsVolume, {
                replacePathByRelativeOne: true,
                liteStats               : true
            });

            await DojoBackendManager.sendResults(exerciseDockerCompose.exitCode, commit, exerciseResultsValidation.exerciseResults!, files, await ArchiveHelper.getBase64(Config.folders.resultsVolume));
        } catch ( error ) {
            console.error(Styles.ERROR(`${ Icon.ERROR } Error while uploading the results`));
            console.error(JSON.stringify(error));
            process.exit(ExerciseCheckerError.UPLOAD);
        }
    }


    /*
     //////////////////////////////////////////////////////////////////////////////////////////////////////////// Step 5:
     - Display results
     - Exit with container exit code
     */
    {
        ClientsSharedExerciseHelper.displayExecutionResults(exerciseResultsValidation.exerciseResults!, exerciseDockerCompose.exitCode, Styles, `\n\n${ Icon.INFO }️ More detailed logs and resources may be available in artifacts`);

        process.exit(exerciseDockerCompose.exitCode);
    }
})();