// Copyright (c) 2024, Compiler Explorer Authors
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

import path from 'node:path';

import {BasicExecutionResult, ExecutableExecutionOptions} from '../../types/execution/execution.interfaces.js';
import * as utils from '../utils.js';
import {LocalExecutionEnvironment} from './base-execution-env.js';

export const AssemblyName = 'CompilerExplorer';

export type DotnetExtraConfiguration = {
    buildConfig: string;
    clrBuildDir: string;
    langVersion: string;
    targetFramework: string;
    corerunPath: string;
};

export class DotnetExecutionEnvironment extends LocalExecutionEnvironment {
    static override get key() {
        return 'local-dotnet';
    }

    override async execBinary(
        executable: string,
        executeParameters: ExecutableExecutionOptions,
        homeDir: string,
        extraConfiguration: DotnetExtraConfiguration,
    ): Promise<BasicExecutionResult> {
        const programDir = path.dirname(executable);
        const programOutputPath = path.join(
            programDir,
            'bin',
            extraConfiguration.buildConfig,
            extraConfiguration.targetFramework,
        );
        const programDllPath = path.join(programOutputPath, `${AssemblyName}.dll`);
        const execOptions = this.getDefaultExecOptions(executeParameters);
        execOptions.maxOutput = this.maxExecOutputSize;
        execOptions.timeoutMs = this.timeoutMs;
        execOptions.ldPath = executeParameters.ldPath;
        execOptions.customCwd = homeDir;
        execOptions.appHome = homeDir;
        execOptions.env = executeParameters.env;
        execOptions.env.DOTNET_EnableWriteXorExecute = '0';
        execOptions.env.DOTNET_CLI_HOME = programDir;
        execOptions.env.CORE_ROOT = extraConfiguration.clrBuildDir;
        execOptions.input = executeParameters.stdin;
        const execArgs = ['-p', 'System.Runtime.TieredCompilation=false', programDllPath, ...executeParameters.args];
        try {
            return await this.execBinaryMaybeWrapped(
                extraConfiguration.corerunPath,
                execArgs,
                execOptions,
                executeParameters,
                homeDir,
            );
        } catch (err: any) {
            if (err.code && err.stderr) {
                return utils.processExecutionResult(err);
            }
            return {
                ...utils.getEmptyExecutionResult(),
                stdout: err.stdout ? utils.parseOutput(err.stdout) : [],
                stderr: err.stderr ? utils.parseOutput(err.stderr) : [],
                code: err.code === undefined ? -1 : err.code,
            };
        }
    }
}
