// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

// This module is the main entry point for use in Node.js environments. For browser environments,
// the "./browser.js" file is the entry point module.

import { createRequire } from "node:module";
import { ProjectLoader } from "../lib/node/qsc_wasm.cjs";
import {
  Compiler,
  ICompiler,
  ICompilerWorker,
  compilerProtocol,
} from "./compiler/compiler.js";
import {
  IDebugService,
  IDebugServiceWorker,
  QSharpDebugService,
  debugServiceProtocol,
} from "./debug-service/debug-service.js";
import {
  ILanguageService,
  ILanguageServiceWorker,
  QSharpLanguageService,
  languageServiceProtocol,
  qsharpLibraryUriScheme,
} from "./language-service/language-service.js";
import { log } from "./log.js";
import { createProxy } from "./workers/node.js";

export { qsharpLibraryUriScheme };

// Only load the Wasm module when first needed, as it may only be used in a Worker,
// and not in the main thread.
type Wasm = typeof import("../lib/node/qsc_wasm.cjs");
let wasm: Wasm | null = null;
const require = createRequire(import.meta.url);

export async function getLibrarySourceContent(
  path: string,
): Promise<string | undefined> {
  if (!wasm) {
    wasm = require("../lib/node/qsc_wasm.cjs") as Wasm;
    return wasm.get_library_source_content(path);
  }
}

export function getCompiler(): ICompiler {
  if (!wasm) {
    wasm = require("../lib/node/qsc_wasm.cjs") as Wasm;
    // Set up logging and telemetry as soon as possible after instantiating
    wasm.initLogging(log.logWithLevel, log.getLogLevel());
    log.onLevelChanged = (level) => wasm?.setLogLevel(level);
  }
  return new Compiler(wasm);
}

export function getProjectLoader(
  readFile: (path: string) => Promise<string | null>,
  loadDirectory: (path: string) => Promise<[string, number][]>,
  getManifest: (path: string) => Promise<{
    manifestDirectory: string;
  } | null>,
): ProjectLoader {
  if (!wasm) {
    wasm = require("../lib/node/qsc_wasm.cjs") as Wasm;
    // Set up logging and telemetry as soon as possible after instantiating
    wasm.initLogging(log.logWithLevel, log.getLogLevel());
    log.onLevelChanged = (level) => wasm?.setLogLevel(level);
  }
  return new wasm.ProjectLoader(readFile, loadDirectory, getManifest);
}

export function getCompilerWorker(): ICompilerWorker {
  return createProxy("../compiler/worker-node.js", compilerProtocol);
}

export function getDebugService(): IDebugService {
  if (!wasm) wasm = require("../lib/node/qsc_wasm.cjs") as Wasm;
  return new QSharpDebugService(wasm);
}

export function getDebugServiceWorker(): IDebugServiceWorker {
  return createProxy("../debug-service/worker-node.js", debugServiceProtocol);
}

export function getLanguageService(
  readFile?: (uri: string) => Promise<string | null>,
  listDir?: (uri: string) => Promise<[string, number][]>,
  getManifest?: (uri: string) => Promise<{
    manifestDirectory: string;
  } | null>,
): ILanguageService {
  if (!wasm) wasm = require("../lib/node/qsc_wasm.cjs") as Wasm;
  return new QSharpLanguageService(wasm, readFile, listDir, getManifest);
}

export function getLanguageServiceWorker(): ILanguageServiceWorker {
  return createProxy(
    "../language-service/worker-node.js",
    languageServiceProtocol,
  );
}

export * as utils from "./utils.js";
