import { substituteDynamicBindingWithValues } from "workers/evaluationSubstitution";
import { EvaluationSubstitutionType } from "entities/DataTree/dataTreeFactory";

describe("substituteDynamicBindingWithValues", () => {
  describe("template substitution", () => {
    it("substitutes strings values", () => {
      const binding = "Hello {{name}}";
      const subBindings = ["Hello ", "{{name}}"];
      const subValues = ["Hello ", "Tester"];
      const expected = "Hello Tester";
      const result = substituteDynamicBindingWithValues(
        binding,
        subBindings,
        subValues,
        EvaluationSubstitutionType.TEMPLATE,
      );

      expect(result).toBe(expected);
    });

    it("substitute number values", () => {
      const binding = "My age is {{age}}";
      const subBindings = ["My age is ", "{{age}}"];
      const subValues = ["My age is ", 16];
      const expected = "My age is 16";
      const result = substituteDynamicBindingWithValues(
        binding,
        subBindings,
        subValues,
        EvaluationSubstitutionType.TEMPLATE,
      );

      expect(result).toBe(expected);
    });

    it("substitute objects/ arrays values", () => {
      const binding = "Response was {{response}}";
      const subBindings = ["Response was ", "{{response}}"];
      const subValues = ["Response was ", { message: "Unauthorised user" }];
      const expected = 'Response was {\\"message\\":\\"Unauthorised user\\"}';
      const result = substituteDynamicBindingWithValues(
        binding,
        subBindings,
        subValues,
        EvaluationSubstitutionType.TEMPLATE,
      );

      expect(result).toBe(expected);
    });

    it("substitute multiple values values", () => {
      const binding =
        "My name is {{name}}. My age is {{age}}. Response: {{response}}";
      const subBindings = [
        "My name is ",
        "{{name}}",
        ". My age is ",
        "{{age}}",
        ". Response: ",
        "{{response}}",
      ];
      const subValues = [
        "My name is ",
        "Tester",
        ". My age is ",
        16,
        ". Response: ",
        { message: "Unauthorised user" },
      ];
      const expected =
        'My name is Tester. My age is 16. Response: {\\"message\\":\\"Unauthorised user\\"}';
      const result = substituteDynamicBindingWithValues(
        binding,
        subBindings,
        subValues,
        EvaluationSubstitutionType.TEMPLATE,
      );

      expect(result).toBe(expected);
    });
  });
  describe("parameter substitution", () => {
    it("replaces bindings with $variables", () => {
      const binding = "SELECT * from {{tableName}} LIMIT {{limit}}";
      const subBindings = [
        "SELECT * from ",
        "{{tableName}}",
        " LIMIT ",
        "{{limit}}",
      ];
      const subValues = ["SELECT * from ", "users", " LIMIT ", 10];
      const expected = {
        value: "SELECT * from $1 LIMIT $2",
        parameters: {
          $1: "users",
          $2: 10,
        },
      };
      const result = substituteDynamicBindingWithValues(
        binding,
        subBindings,
        subValues,
        EvaluationSubstitutionType.PARAMETER,
      );

      expect(result).toHaveProperty("value");
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore
      expect(result.value).toBe(expected.value);
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore
      expect(result.parameters).toStrictEqual(expected.parameters);
    });

    it("removed quotes around bindings", () => {
      const binding =
        'SELECT * from users WHERE lastname = "{{lastname}}" LIMIT {{limit}}';
      const subBindings = [
        'SELECT * from users WHERE lastname = "',
        "{{lastname}}",
        '" LIMIT ',
        "{{limit}}",
      ];
      const subValues = [
        'SELECT * from users WHERE lastname = "',
        "Smith",
        '" LIMIT ',
        10,
      ];
      const expected = {
        value: "SELECT * from users WHERE lastname = $1 LIMIT $2",
        parameters: {
          $1: "Smith",
          $2: 10,
        },
      };
      const result = substituteDynamicBindingWithValues(
        binding,
        subBindings,
        subValues,
        EvaluationSubstitutionType.PARAMETER,
      );

      expect(result).toHaveProperty("value");
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore
      expect(result.value).toBe(expected.value);
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore
      expect(result.parameters).toStrictEqual(expected.parameters);
    });

    it("stringifies objects and arrays", () => {
      const binding = "SELECT * from {{testObject}} WHERE {{testArray}}";
      const subBindings = [
        "SELECT * from ",
        "{{testObject}}",
        " WHERE ",
        "{{testArray}}",
      ];
      const subValues = [
        "SELECT * from ",
        { name: "tester" },
        " WHERE ",
        [42, "meaning", false],
      ];
      const expected = {
        value: "SELECT * from $1 WHERE $2",
        parameters: {
          $1: `{\n  \"name\": \"tester\"\n}`,
          $2: `[\n  42,\n  \"meaning\",\n  false\n]`,
        },
      };
      const result = substituteDynamicBindingWithValues(
        binding,
        subBindings,
        subValues,
        EvaluationSubstitutionType.PARAMETER,
      );

      expect(result).toHaveProperty("value");
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore
      expect(result.value).toBe(expected.value);
      // eslint-disable-next-line @typescript-eslint/ban-ts-comment
      // @ts-ignore
      expect(result.parameters).toStrictEqual(expected.parameters);
    });
  });
  describe("smart substitution", () => {
    it("substitutes strings, numbers, boolean, undefined, null values correctly", () => {
      const binding = `{
        "name": {{name}},
        "age": {{age}},
        "isHuman": {{isHuman}},
        "wrongBinding": {{wrongBinding}},
        "emptyBinding": {{emptyBinding}},
      }`;
      const subBindings = [
        '{\n        "name": ',
        "{{name}}",
        ',\n        "age": ',
        "{{age}}",
        ',\n        "isHuman": ',
        "{{isHuman}}",
        ',\n        "wrongBinding": ',
        "{{wrongBinding}}",
        ',\n        "emptyBinding": ',
        "{{emptyBinding}}",
        ",\n      }",
      ];
      const subValues = [
        '{\n        "name": ',
        "Tester",
        ',\n        "age": ',
        42,
        ',\n        "isHuman": ',
        false,
        ',\n        "wrongBinding": ',
        undefined,
        ',\n        "emptyBinding": ',
        null,
        ",\n      }",
      ];
      const expected = `{
        "name": "Tester",
        "age": 42,
        "isHuman": false,
        "wrongBinding": undefined,
        "emptyBinding": null,
      }`;
      const result = substituteDynamicBindingWithValues(
        binding,
        subBindings,
        subValues,
        EvaluationSubstitutionType.SMART_SUBSTITUTE,
      );

      expect(result).toBe(expected);
    });

    it("substitute objects/ arrays values", () => {
      const binding = `{\n  "data": {{formData}}\n}`;
      const subBindings = ["{\n  data: ", "{{formData}}", "\n}"];
      const subValues = [
        '{\n  "data": ',
        {
          name: "Tester",
          age: 42,
          isHuman: false,
          wrongBinding: undefined,
          emptyBinding: null,
        },
        "\n}",
      ];
      const expected =
        '{\n  "data": {\n  "name": "Tester",\n  "age": 42,\n  "isHuman": false,\n  "emptyBinding": null\n}\n}';
      const result = substituteDynamicBindingWithValues(
        binding,
        subBindings,
        subValues,
        EvaluationSubstitutionType.SMART_SUBSTITUTE,
      );

      expect(result).toBe(expected);
    });

    it("substitute correctly when quotes are surrounding the binding", () => {
      const binding = `{
        "name": "{{name}}",
        "age": "{{age}}",
        isHuman: {{isHuman}},
        "wrongBinding": {{wrongBinding}},
        "emptyBinding": "{{emptyBinding}}",
      }`;
      const subBindings = [
        '{\n        "name": "',
        "{{name}}",
        '",\n        "age": "',
        "{{age}}",
        '",\n        isHuman: ',
        "{{isHuman}}",
        ',\n        "wrongBinding": ',
        "{{wrongBinding}}",
        ',\n        "emptyBinding": "',
        "{{emptyBinding}}",
        '",\n      }',
      ];
      const subValues = [
        '{\n        "name": "',
        "Tester",
        '",\n        "age": "',
        42,
        '",\n        isHuman: ',
        false,
        ',\n        "wrongBinding": ',
        undefined,
        ',\n        "emptyBinding": "',
        null,
        '",\n      }',
      ];
      const expected = `{
        "name": "Tester",
        "age": 42,
        isHuman: false,
        "wrongBinding": undefined,
        "emptyBinding": null,
      }`;
      const result = substituteDynamicBindingWithValues(
        binding,
        subBindings,
        subValues,
        EvaluationSubstitutionType.SMART_SUBSTITUTE,
      );

      expect(result).toBe(expected);
    });

    it("escapes strings before substitution", () => {
      const binding = `{\n  "paragraph": {{paragraph}},\n}`;
      const subBindings = ['{\n  "paragraph": ', "{{paragraph}}", ",\n}"];
      const subValues = [
        '{\n  "paragraph": ',
        `This is a \f string \b with \n many different " characters that are not \n all. these \r\t`,
        ",\n}",
      ];
      const expected = `{\n  "paragraph": "This is a \\f string \\b with \\n many different \\" characters that are not \\n all. these \\r\\t",\n}`;
      const result = substituteDynamicBindingWithValues(
        binding,
        subBindings,
        subValues,
        EvaluationSubstitutionType.SMART_SUBSTITUTE,
      );

      expect(result).toBe(expected);
    });

    it("throws error when only binding is provided in parameter substitution", () => {
      const binding = `{{ appsmith }}`;
      const subBindings = ["{{appsmith}}"];
      const subValues = [{ test: "object" }];
      expect(() =>
        substituteDynamicBindingWithValues(
          binding,
          subBindings,
          subValues,
          EvaluationSubstitutionType.PARAMETER,
        ),
      ).toThrowError();
    });
  });
});
