import { DocumentState, EmptyFileSystem, TextDocument } from "langium";
import * as assert from "node:assert";
import stripIndent from "strip-indent";
import { DiagnosticSeverity } from "vscode-languageserver-types";
import { URI, Utils } from "vscode-uri";
import { createLanguageServices } from "../module.js";
export function createTestServices(workspace = "file:///test/workspace") {
  const services = createLanguageServices(EmptyFileSystem).likec4;
  const metaData = services.LanguageMetaData;
  const langiumDocuments = services.shared.workspace.LangiumDocuments;
  const documentBuilder = services.shared.workspace.DocumentBuilder;
  const modelBuilder = services.likec4.ModelBuilder;
  const workspaceUri = URI.parse(workspace);
  const formatter = services.lsp.Formatter;
  const workspaceFolder = {
    name: "test",
    uri: workspaceUri.toString()
  };
  let isInitialized = false;
  let documentIndex = 1;
  const parse = async (input, uri) => {
    if (!isInitialized) {
      await services.shared.workspace.WorkspaceLock.write(async (_cancelToken) => {
        if (isInitialized) {
          return;
        }
        isInitialized = true;
        services.shared.workspace.WorkspaceManager.initialize({
          capabilities: {},
          processId: null,
          rootUri: null,
          workspaceFolders: [workspaceFolder]
        });
        await services.shared.workspace.WorkspaceManager.initializeWorkspace([workspaceFolder]);
      });
    }
    const docUri = Utils.resolvePath(
      workspaceUri,
      "./src/",
      uri ?? `${documentIndex++}${metaData.fileExtensions[0]}`
    );
    const document = services.shared.workspace.LangiumDocumentFactory.fromString(
      stripIndent(input),
      docUri
    );
    langiumDocuments.addDocument(document);
    await services.shared.workspace.WorkspaceLock.write(async (_cancelToken) => {
      await documentBuilder.build([document], { validation: false });
    });
    return document;
  };
  const validate = async (input, uri) => {
    const document = typeof input === "string" ? await parse(input, uri) : input;
    await services.shared.workspace.WorkspaceLock.write(async (_cancelToken) => {
      await documentBuilder.build([document], { validation: true });
    });
    const diagnostics = document.diagnostics ?? [];
    const warnings = diagnostics.flatMap((d) => d.severity === DiagnosticSeverity.Warning ? d.message : []);
    const errors = diagnostics.flatMap((d) => d.severity === DiagnosticSeverity.Error ? d.message : []);
    return {
      document,
      diagnostics,
      warnings,
      errors
    };
  };
  const format = async (input, uri) => {
    const document = typeof input === "string" ? await parse(input, uri) : input;
    await services.shared.workspace.WorkspaceLock.write(async (_cancelToken) => {
      await documentBuilder.build([document], { validation: true });
    });
    const edits = await formatter?.formatDocument(
      document,
      {
        options: { tabSize: 2, insertSpaces: true },
        textDocument: { uri: document.uri.toString() }
      }
    );
    return TextDocument.applyEdits(document.textDocument, edits ?? []);
  };
  const validateAll = async () => {
    await services.shared.workspace.WorkspaceLock.write(async (_cancelToken) => {
      const docs2 = langiumDocuments.all.toArray();
      await documentBuilder.build(docs2, { validation: true });
    });
    await documentBuilder.waitUntil(DocumentState.Validated);
    const docs = langiumDocuments.all.toArray();
    assert.ok(docs.length > 0, "no documents to validate");
    const diagnostics = docs.flatMap((doc) => doc.diagnostics ?? []);
    const warnings = diagnostics.flatMap((d) => d.severity === DiagnosticSeverity.Warning ? d.message : []);
    const errors = diagnostics.flatMap((d) => d.severity === DiagnosticSeverity.Error ? d.message : []);
    return {
      diagnostics,
      errors,
      warnings
    };
  };
  const buildModel = async () => {
    await validateAll();
    const model = await modelBuilder.buildComputedModel();
    if (!model) throw new Error("No model found");
    return model;
  };
  const resetState = async () => {
    await services.shared.workspace.WorkspaceLock.write(async (cancelToken) => {
      const docs = langiumDocuments.all.toArray().map((doc) => doc.uri);
      await documentBuilder.update([], docs, cancelToken);
    });
  };
  return {
    services,
    parse,
    validate,
    validateAll,
    buildModel,
    resetState,
    format
  };
}
