/*
 * host.ts
 * Copyright (c) Microsoft Corporation.
 * Licensed under the MIT license.
 *
 * Provides access to the host environment the language service is running on.
 */

import { CancellationToken } from 'vscode-languageserver';

import { ImportLogger } from '../analyzer/importLogger';
import { PythonPathResult } from '../analyzer/pythonPathUtils';
import { PythonPlatform } from './configOptions';
import { PythonVersion } from './pythonVersion';
import { Uri } from './uri/uri';

export const enum HostKind {
    FullAccess,
    LimitedAccess,
    NoAccess,
}

export interface ScriptOutput {
    stdout: string;
    stderr: string;

    // Optional output that contains both stdout and stderr interleaved in choronological order.
    output?: string;
    exitCode?: number;
}

/**
 * Options for spawning a long-lived child process.
 * Intentionally avoids Node.js-specific types so this interface can be
 * referenced from platform-neutral code.
 */
export interface ProcessSpawnOptions {
    cwd?: string;
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    env?: Record<string, string | undefined>;
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    stdio?: any;
}

/**
 * Minimal abstraction over a spawned child process.
 * Fields are typed as `any` for stdin/stdout/stderr to keep this interface
 * free of Node.js-specific stream imports while still allowing callers in
 * Node-only code to use them as streams.
 */
export interface SpawnedProcess {
    readonly pid?: number;
    readonly exitCode: number | null;
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    readonly stdout: any;
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    readonly stdin: any;
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    readonly stderr: any;
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    on(event: string, listener: (...args: any[]) => void): void;
}

export interface Host {
    readonly kind: HostKind;
    getPythonSearchPaths(pythonPath?: Uri, failureLogger?: ImportLogger, cwd?: Uri): PythonPathResult;
    getPythonVersion(pythonPath?: Uri, failureLogger?: ImportLogger): PythonVersion | undefined;
    getPythonPlatform(failureLogger?: ImportLogger): PythonPlatform | undefined;
    runScript(
        pythonPath: Uri | undefined,
        script: Uri,
        args: string[],
        cwd: Uri,
        token: CancellationToken
    ): Promise<ScriptOutput>;
    runSnippet(
        pythonPath: Uri | undefined,
        code: string,
        args: string[],
        cwd: Uri,
        token: CancellationToken,
        forceIsolated?: boolean
    ): Promise<ScriptOutput>;
    /**
     * Spawn a long-lived child process and return an abstraction over it.
     * Hosts with `LimitedAccess` or `NoAccess` kind must return `undefined`
     * to prevent process spawning in restricted environments.
     */
    spawnProcess(exe: string, args: string[], options: ProcessSpawnOptions): SpawnedProcess | undefined;
}

export class NoAccessHost implements Host {
    get kind(): HostKind {
        return HostKind.NoAccess;
    }

    getPythonSearchPaths(pythonPath?: Uri, failureLogger?: ImportLogger, cwd?: Uri): PythonPathResult {
        failureLogger?.log('No access to python executable.');

        return {
            paths: [],
            prefix: undefined,
        };
    }

    getPythonVersion(pythonPath?: Uri, failureLogger?: ImportLogger): PythonVersion | undefined {
        return undefined;
    }

    getPythonPlatform(failureLogger?: ImportLogger): PythonPlatform | undefined {
        return undefined;
    }

    async runScript(
        pythonPath: Uri | undefined,
        scriptPath: Uri,
        args: string[],
        cwd: Uri,
        token: CancellationToken
    ): Promise<ScriptOutput> {
        return { stdout: '', stderr: '' };
    }

    async runSnippet(
        pythonPath: Uri | undefined,
        code: string,
        args: string[],
        cwd: Uri,
        token: CancellationToken
    ): Promise<ScriptOutput> {
        return { stdout: '', stderr: '' };
    }

    spawnProcess(_exe: string, _args: string[], _options: ProcessSpawnOptions): SpawnedProcess | undefined {
        return undefined;
    }
}

export type HostFactory = () => Host;
