import {
  clientContextValidation,
  dataModelsValidation,
  securityContextValidation,
} from "./validate";
import * as fs from "node:fs/promises";

const startMock = {
  succeed: vi.fn(),
  fail: vi.fn(),
};

vi.mock("@embeddable.com/sdk-utils", () => ({
  errorFormatter: vi.fn(),
  findFiles: vi.fn(),
}));

const failMock = vi.fn();

const validYaml = `cubes:
  - name: customers
    title: My customers
    data_source: default
    sql_table: public.customers

    dimensions:
      - name: id
        sql: id
        type: number
        primary_key: true`;

const invalidYaml = `${validYaml}
    measures:
      - name: count
        type: count
        title: '# of customers'
      - name: test
        type: number
        sql: {count} / 10.0`;

const securityContextYaml = `
- name: Example customer 1
  securityContext:
    country: United States
  environment: default`;

const clientContextYaml = `
- name: blue
  clientContext:
    color: blue`;

vi.mock("ora", () => ({
  default: () => ({
    start: vi.fn().mockImplementation(() => startMock),
    info: vi.fn(),
    fail: failMock,
  }),
}));

vi.mock("node:fs/promises", () => ({
  readFile: vi.fn(),
  writeFile: vi.fn(),
  access: vi.fn(),
  mkdir: vi.fn(),
}));

describe("validate", () => {
  describe("dataModelsValidation", () => {
    it("should return an empty array if the data models are valid", async () => {
      vi.mocked(fs.readFile).mockImplementation(async () => {
        return validYaml;
      });
      const filesList: [string, string][] = [
        ["valid-cube.yaml", "path/to/file"],
      ];
      const result = await dataModelsValidation(filesList);
      expect(result).toEqual([]);
    });

    it("should return an array of error messages if the data models are invalid", async () => {
      vi.mocked(fs.readFile).mockImplementation(async () => {
        return "";
      });
      const filesList: [string, string][] = [
        ["invalid-cube.yaml", "path/to/file"],
      ];
      const result = await dataModelsValidation(filesList);
      expect(result).toEqual([
        "path/to/file: At least one cubes or views must be defined",
      ]);
    });

    it("should return an array of error messages if the data models parsing fails", async () => {
      vi.mocked(fs.readFile).mockImplementation(async () => {
        return invalidYaml;
      });
      const filesList: [string, string][] = [
        ["invalid-cube.yaml", "path/to/file"],
      ];
      const result = await dataModelsValidation(filesList);
      expect(result).toMatchInlineSnapshot(`
        [
          "path/to/file: Unexpected scalar at node end at line 18, column 22:

                sql: {count} / 10.0
                             ^^^^^^
        ",
        ]
      `);
    });
  });

  describe("securityContextValidation", () => {
    it("should return an empty array if the security context is valid", async () => {
      vi.mocked(fs.readFile).mockImplementation(async () => {
        return securityContextYaml;
      });
      const filesList: [string, string][] = [
        ["valid-security-context.json", "path/to/file"],
      ];
      const result = await securityContextValidation(filesList);
      expect(result).toEqual([]);
    });

    it("should return an array of error messages if the security context is invalid", async () => {
      vi.mocked(fs.readFile).mockImplementation(async () => {
        return `${securityContextYaml} ${securityContextYaml}`;
      });
      const filesList: [string, string][] = [
        ["invalid-security-context.json", "path/to/file"],
      ];
      const result = await securityContextValidation(filesList);
      expect(result).toEqual([
        'path/to/file: security context with name "Example customer 1" already exists',
      ]);
    });
  });

  describe("clientContextValidation", () => {
    it("should return an empty array if the client context is valid", async () => {
      vi.mocked(fs.readFile).mockImplementation(async () => {
        return clientContextYaml;
      });
      const filesList: [string, string][] = [
        ["valid-client-context.json", "path/to/file"],
      ];
      const result = await clientContextValidation(filesList);
      expect(result).toEqual([]);
    });

    it("should return an array of error messages if the client context is invalid", async () => {
      vi.mocked(fs.readFile).mockImplementation(async () => {
        return `${clientContextYaml} ${clientContextYaml}`;
      });
      const filesList: [string, string][] = [
        ["invalid-client-context.json", "path/to/file"],
      ];
      const result = await clientContextValidation(filesList);
      expect(result).toEqual([
        'path/to/file: client context with name "blue" already exists',
      ]);
    });
  });
});
