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

import path from "path";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { bicepCli, pathToExampleFile, writeTempFile } from "../utils/fs";
import { Bicep } from "@azure/bicep-rpc-client";

describe("bicep jsonrpc", () => {
  let bicep: Bicep;

  beforeAll(async () => (bicep = await Bicep.initialize(bicepCli)));

  afterAll(() => bicep.dispose());

  it("should return a version number", async () => {
    const result = await bicep.version();

    expect(result).toMatch(/^\d+\.\d+\.\d+/);
  });

  it("should build a bicep file", async () => {
    const result = await bicep.compile({ path: pathToExampleFile("101", "aks.prod", "main.bicep") });

    expect(result.success).toBeTruthy();
    expect(result.contents?.length).toBeGreaterThan(0);
  });

  it("should build a bicepparam file", async () => {
    const result = await bicep.compileParams({
      path: pathToExampleFile("bicepparam", "main.bicepparam"), parameterOverrides: {
        foo: "OVERRIDDEN",
      }
    });

    expect(result.success).toBeTruthy();
    expect(result.parameters?.length).toBeGreaterThan(0);
    expect(JSON.parse(result.parameters!).parameters.foo.value).toBe("OVERRIDDEN");
  });

  it("should return a deployment graph", async () => {
    const bicepPath = writeTempFile(
      "jsonrpc-graph",
      "metadata.bicep",
      `
    resource foo 'My.Rp/foo@2020-01-01' = {
      name: 'foo'
    }

    resource bar 'My.Rp/foo@2020-01-01' existing = {
      name: 'bar'
      dependsOn: [foo]
    }

    resource baz 'My.Rp/foo@2020-01-01' = {
      name: 'baz'
      dependsOn: [bar]
    }
    `,
    );

    const result = await bicep.getDeploymentGraph({ path: bicepPath });

    expect(result.nodes).toHaveLength(3);
    expect(result.edges).toHaveLength(2);
  });

  it("should return diagnostics if the bicep file has errors", async () => {
    const filePath = pathToExampleFile("101", "aks.prod", "flawed.bicep");
    const result = await bicep.compile({ path: filePath });

    expect(result.success).toBeFalsy();
    expect(result.contents).toBeUndefined();

    const error = result.diagnostics.filter((x) => x.level === "Error")[0];

    expect(error).toStrictEqual({
      source: filePath,
      range: { end: { char: 34, line: 32 }, start: { char: 22, line: 32 } },
      level: "Error",
      code: "BCP057",
      message: 'The name "osDiskSizeGb" does not exist in the current context.',
    });
  });

  it("should return metadata for a bicep file", async () => {
    const bicepPath = writeTempFile(
      "jsonrpc-metadata",
      "metadata.bicep",
      `
    metadata description = 'my file'

    @export()
    @description('baz type')
    type baz = {}

    @description('foo param')
    param foo string

    @description('bar output')
    output bar string = foo
    `,
    );

    const result = await bicep.getMetadata({ path: bicepPath });

    expect(result.metadata.filter((x) => x.name === "description")[0].value).toBe("my file");
    expect(result.parameters.filter((x) => x.name === "foo")[0]).toStrictEqual({
      description: "foo param",
      name: "foo",
      range: { end: { char: 20, line: 8 }, start: { char: 4, line: 7 } },
      type: { name: "string" },
    });
    expect(result.outputs.filter((x) => x.name === "bar")[0]).toStrictEqual({
      description: "bar output",
      name: "bar",
      range: { end: { char: 27, line: 11 }, start: { char: 4, line: 10 } },
      type: { name: "string" },
    });
    expect(result.exports.filter((x) => x.name === "baz")[0]).toStrictEqual({
      description: "baz type",
      kind: "TypeAlias",
      name: "baz",
      range: { end: { char: 17, line: 5 }, start: { char: 4, line: 3 } },
    });
  });

  it("should return file references for a bicep file", async () => {
    const bicepParamPath = writeTempFile(
      "jsonrpc-refs",
      "main.bicepparam",
      `
using 'main.bicep'

param foo = 'foo'
`,
    );
    writeTempFile(
      "jsonrpc-refs",
      "main.bicep",
      `
param foo string

var test = loadTextContent('invalid.txt')
var test2 = loadTextContent('valid.txt')
`,
    );
    writeTempFile(
      "jsonrpc-refs",
      "valid.txt",
      `
hello!
`,
    );
    writeTempFile(
      "jsonrpc-refs",
      "bicepconfig.json",
      `
{}
`,
    );

    const result = await bicep.getFileReferences({ path: bicepParamPath });

    expect(result.filePaths).toStrictEqual([
      path.join(bicepParamPath, "../bicepconfig.json"),
      path.join(bicepParamPath, "../invalid.txt"),
      path.join(bicepParamPath, "../main.bicep"),
      path.join(bicepParamPath, "../main.bicepparam"),
      path.join(bicepParamPath, "../valid.txt"),
    ]);
  });

  it("should format a bicep file", async () => {
    const bicepPath = writeTempFile(
      "jsonrpc-format",
      "format.bicep",
      `
param foo string
param bar int = 42

resource storage 'Microsoft.Storage/storageAccounts@2022-09-01' = {
name: 'mystorageaccount'
location: 'East US'
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
}
    `,
    );

    const result = await bicep.format({ path: bicepPath });

    expect(result.contents).toBeDefined();
    expect(result.contents.includes("param foo string")).toBeTruthy();
    expect(result.contents.includes("  name: 'mystorageaccount'")).toBeTruthy();
  });
});