import fs from "fs";
import { searchAssetByKind } from "./asset-searchByKind-helper";
import {
  isDirOrFileExists,
  isDirectory,
  getSubDirectory,
  readFile,
  isYamlFile,
} from "../common/fs-helper";
import { showWarning, showError } from "../common/message-helper";
import { readMultiYaml } from "../common/yaml-helper";
import { equalsIgnoreCase } from "../common/data-helper";
import { BaseAsset } from "../../model/assets-model";
import {
  DIRECTORY_DOESNT_EXIST,
  ERROR_IN_SEARCH_OF_ASSET,
  NO_ENTRIES_FOUND_FOR_KIND,
  NO_ASSET_METADATA,
  IS_FOUND_IN,
} from "../../constants/message-constants";

jest.mock("../common/fs-helper", () => ({
  isDirOrFileExists: jest.fn(),
  isDirectory: jest.fn(),
  getSubDirectory: jest.fn(),
  readFile: jest.fn(),
  isYamlFile: jest.fn(),
  normalizePath: jest.fn(),
}));

jest.mock("../common/yaml-helper", () => ({
  readMultiYaml: jest.fn(),
}));

jest.mock("../common/message-helper", () => ({
  showWarning: jest.fn(),
  showError: jest.fn(),
}));

jest.mock("../common/data-helper", () => ({
  equalsIgnoreCase: jest.fn(),
  isNullOrUndefined: jest.fn(),
}));

jest.mock("fs", () => ({
  readdirSync: jest.fn(),
}));

describe("Asset- searchByKind helper test suite", () => {
  const kindToSearch = "SampleKind";
  const rootDirPath = "/root/dir";
  const projectNames = "project1,project2";
  const projectDirPath = "/root/dir/project1";

  const mockDirent: fs.Dirent = {
    name: "sample.yaml",
    parentPath: projectDirPath,
    isDirectory: () => false,
    isFile: () => true,
    isBlockDevice: () => false,
    isCharacterDevice: () => false,
    isFIFO: () => false,
    isSocket: () => false,
    isSymbolicLink: () => false,
    [Symbol.toStringTag]: "Dirent",
  } as unknown as fs.Dirent;

  beforeEach(() => {
    jest.clearAllMocks();
  });

  it("should return formatted metadata for valid projects and assets", async () => {
    const assets = [
      {
        kind: "SampleKind",
        metadata: { namespace: "ns", name: "name1", version: "v1" },
      } as BaseAsset,
    ];

    (isDirOrFileExists as jest.Mock).mockReturnValue(true);
    (isDirectory as jest.Mock).mockReturnValue(true);
    (getSubDirectory as jest.Mock).mockReturnValue(projectDirPath);
    (fs.readdirSync as jest.Mock).mockReturnValue([mockDirent]);
    (isYamlFile as jest.Mock).mockReturnValue(true);
    (readFile as jest.Mock).mockReturnValue("file content");
    (readMultiYaml as jest.Mock).mockReturnValue(assets);
    (equalsIgnoreCase as jest.Mock).mockReturnValue(true);

    const result = await searchAssetByKind(
      kindToSearch,
      rootDirPath,
      projectNames
    );

    expect(result).toEqual({
      project1: "ns:name1:v1",
      project2: "ns:name1:v1",
    });
    expect(showWarning).not.toHaveBeenCalled();
    expect(showError).not.toHaveBeenCalled();
  });

  it("should show a warning if the directory does not exist", async () => {
    (isDirOrFileExists as jest.Mock).mockReturnValue(false);
    (getSubDirectory as jest.Mock).mockReturnValue(projectDirPath);

    const result = await searchAssetByKind(
      kindToSearch,
      rootDirPath,
      projectNames
    );

    expect(result).toEqual({});
    expect(showWarning).toHaveBeenCalledWith(
      `${DIRECTORY_DOESNT_EXIST} ${projectDirPath}`
    );
    expect(showError).toHaveBeenCalled();
  });

  it("should show a warning if no entries found for the kind", async () => {
    (isDirOrFileExists as jest.Mock).mockReturnValue(true);
    (isDirectory as jest.Mock).mockReturnValue(true);
    (getSubDirectory as jest.Mock).mockReturnValue(projectDirPath);
    (fs.readdirSync as jest.Mock).mockReturnValue([]);

    const result = await searchAssetByKind(
      kindToSearch,
      rootDirPath,
      projectNames
    );

    expect(result).toEqual({});
    expect(showWarning).toHaveBeenCalledWith(
      `${NO_ENTRIES_FOUND_FOR_KIND} - '${kindToSearch}' ${IS_FOUND_IN} '${projectDirPath}'`
    );
    expect(showError).toHaveBeenCalled();
  });

  it("should show an error if there is an error in searching the asset", async () => {
    (isDirOrFileExists as jest.Mock).mockReturnValue(true);
    (isDirectory as jest.Mock).mockReturnValue(true);
    (getSubDirectory as jest.Mock).mockReturnValue(projectDirPath);
    (fs.readdirSync as jest.Mock).mockImplementation(() => {
      throw new Error("FS error");
    });

    await expect(
      searchAssetByKind(kindToSearch, rootDirPath, projectNames)
    ).rejects.toThrow("FS error");

    expect(showError).toHaveBeenCalledWith(
      `${ERROR_IN_SEARCH_OF_ASSET} ${kindToSearch}: FS error`
    );
  });

  it("should show an error if no asset metadata is found", async () => {
    (isDirOrFileExists as jest.Mock).mockReturnValue(true);
    (isDirectory as jest.Mock).mockReturnValue(true);
    (getSubDirectory as jest.Mock).mockReturnValue(projectDirPath);
    (fs.readdirSync as jest.Mock).mockReturnValue([mockDirent]);
    (isYamlFile as jest.Mock).mockReturnValue(true);
    (readFile as jest.Mock).mockReturnValue("file content");
    (readMultiYaml as jest.Mock).mockReturnValue([]);

    const result = await searchAssetByKind(
      kindToSearch,
      rootDirPath,
      projectNames
    );

    expect(result).toEqual({});
    expect(showError).toHaveBeenCalledWith(NO_ASSET_METADATA);
  });
});
