/**
 * Copyright Super iPaaS Integration LLC, an IBM Company 2024
 */
import { AssetCache } from "./asset-cache.js";
import { BaseAsset } from "../model/assets-model.js";
import { AssetCacheModel } from "../model/asset-cache-model.js";
import { COLON } from "../constants/app-constants.js";

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

describe("AssetCache test suite", () => {
  let assetCache: AssetCache;
  const mockAsset: BaseAsset = {
    kind: "api",
    metadata: {
      name: "mockAssetName",
      namespace: "mockNamespace",
      version: "v1",
    },
  } as unknown as BaseAsset;

  beforeEach(() => {
    assetCache = AssetCache.getInstance();
    assetCache.clear();
  });

  it("should create a singleton instance", () => {
    const instance1 = AssetCache.getInstance();
    const instance2 = AssetCache.getInstance();
    expect(instance1).toBe(instance2);
  });
  it("should return newly added unprocessed assets", () => {
    const assetRefValue = `${mockAsset.metadata.namespace}${COLON}${mockAsset.metadata.name}${COLON}${mockAsset.metadata.version}`;
    const cacheModel: AssetCacheModel = {
      kind: "api",
      ref: assetRefValue,
      isNewlyAdded: true,
    };
    assetCache.checkAndMarkAsUnProcessed(cacheModel);
    const newlyAddedAssets = assetCache.getNewlyAddedUnProcessedAssets();
    expect(newlyAddedAssets.size).toBe(1);
    expect(Array.from(newlyAddedAssets)[0]).toMatchObject(cacheModel);
  });
  it("should able to mark all unprocessed assets as unchecked", () => {
    const assetRefValue = `${mockAsset.metadata.namespace}${COLON}${mockAsset.metadata.name}${COLON}${mockAsset.metadata.version}`;
    const cacheModel: AssetCacheModel = {
      kind: "api",
      ref: assetRefValue,
      isNewlyAdded: false,
    };
    assetCache.checkAndMarkAsUnProcessed(cacheModel);

    assetCache.markAllUnProcessedAssetAsUnchecked();
    const unprocessedAssets = assetCache.getUnProcessedAssets();
    unprocessedAssets.forEach((asset) => {
      expect(asset.isNewlyAdded).toBe(true);
    });
  });

  it("should able to clear all processed and unprocessed assets", () => {
    const assetRefValue = `${mockAsset.metadata.namespace}${COLON}${mockAsset.metadata.name}${COLON}${mockAsset.metadata.version}`;
    const cacheModel: AssetCacheModel = {
      kind: "api",
      ref: assetRefValue,
      isNewlyAdded: true,
    };
    assetCache.checkAndMarkAsUnProcessed(cacheModel);
    assetCache.markAsProcessed(mockAsset);

    assetCache.clear();
    expect(assetCache.getUnProcessedAssets().size).toBe(0);
    expect(assetCache.getProcessedAsset("api", assetRefValue)).toBeUndefined();
  });
  it("should able to mark asset as processed and remove from unprocessed ", () => {
    const assetRefValue = `${mockAsset.metadata.namespace}${COLON}${mockAsset.metadata.name}${COLON}${mockAsset.metadata.version}`;
    const cacheModel: AssetCacheModel = {
      kind: "api",
      ref: assetRefValue,
      isNewlyAdded: true,
    };
    assetCache.checkAndMarkAsUnProcessed(cacheModel);
    assetCache.markAsProcessed(mockAsset);

    expect(assetCache.isProcessed("api", assetRefValue)).toBe(true);
    expect(assetCache.isToBeProcessed("api", assetRefValue)).toBe(false);
  });

  it("should mark unprocessed asset as checked", () => {
    const assetRefValue = `${mockAsset.metadata.namespace}${COLON}${mockAsset.metadata.name}${COLON}${mockAsset.metadata.version}`;
    const cacheModel: AssetCacheModel = {
      kind: "api",
      ref: assetRefValue,
      isNewlyAdded: true,
    };
    assetCache.checkAndMarkAsUnProcessed(cacheModel);
    assetCache.markUnProcessedAssetAsChecked(cacheModel);

    const unProcessedAssets = assetCache.getNewlyAddedUnProcessedAssets();
    expect(unProcessedAssets.size).toBe(0);
  });

  it("should return true when asset is processed", () => {
    const assetRefValue = `${mockAsset.metadata.namespace}${COLON}${mockAsset.metadata.name}${COLON}${mockAsset.metadata.version}`;
    assetCache.markAsProcessed(mockAsset);
    
    expect(assetCache.isProcessed("api", assetRefValue)).toBe(true);
  });

  it("should return false when asset is not processed", () => {
    const assetRefValue = `${mockAsset.metadata.namespace}${COLON}${mockAsset.metadata.name}${COLON}${mockAsset.metadata.version}`;
    
    expect(assetCache.isProcessed("api", assetRefValue)).toBe(false);
  });

  it("should add and process an asset without namespace", () => {
    const assetWithoutNamespace: BaseAsset = {
      kind: "api",
      metadata: {
        name: "mockAssetNameNoNamespace",
        version: "v1",
      },
    } as unknown as BaseAsset;

    const assetRefValue = `${assetWithoutNamespace.metadata.name}${COLON}${assetWithoutNamespace.metadata.version}`;
    const cacheModel: AssetCacheModel = {
      kind: "api",
      ref: assetRefValue,
      isNewlyAdded: true,
    };

    assetCache.checkAndMarkAsUnProcessed(cacheModel);
    expect(assetCache.isToBeProcessed("api", assetRefValue)).toBe(true);
    
    assetCache.markAsProcessed(assetWithoutNamespace);
    expect(assetCache.isProcessed("api", assetRefValue)).toBe(true);
  });

  it("should return undefined for non-existent processed asset", () => {
    const nonExistentAssetRefValue = `nonexistent${COLON}v1`;
    const processedAsset = assetCache.getProcessedAsset("api", nonExistentAssetRefValue);
    expect(processedAsset).toBeUndefined();
  });

  it("should not add duplicate entries to unprocessed assets", () => {
    const assetRefValue = `${mockAsset.metadata.namespace}${COLON}${mockAsset.metadata.name}${COLON}${mockAsset.metadata.version}`;
    const cacheModel: AssetCacheModel = {
      kind: "api",
      ref: assetRefValue,
      isNewlyAdded: true,
    };

    assetCache.checkAndMarkAsUnProcessed(cacheModel);
    assetCache.checkAndMarkAsUnProcessed(cacheModel); // Attempt to add the same asset again

    const unprocessedAssets = assetCache.getUnProcessedAssets();
    expect(unprocessedAssets.size).toBe(1); // Should only have one instance
  });

  it("should not affect other unprocessed assets when marking one as checked", () => {
    const assetRefValue1 = `${mockAsset.metadata.namespace}${COLON}${mockAsset.metadata.name}${COLON}${mockAsset.metadata.version}`;
    const assetRefValue2 = `${mockAsset.metadata.namespace}${COLON}anotherAsset${COLON}${mockAsset.metadata.version}`;

    const cacheModel1: AssetCacheModel = {
      kind: "api",
      ref: assetRefValue1,
      isNewlyAdded: true,
    };

    const cacheModel2: AssetCacheModel = {
      kind: "api",
      ref: assetRefValue2,
      isNewlyAdded: true,
    };

    assetCache.checkAndMarkAsUnProcessed(cacheModel1);
    assetCache.checkAndMarkAsUnProcessed(cacheModel2);

    // Mark the first asset as checked
    assetCache.markUnProcessedAssetAsChecked(cacheModel1);

    const unprocessedAssets = assetCache.getNewlyAddedUnProcessedAssets();
    expect(unprocessedAssets.size).toBe(1);
    expect(Array.from(unprocessedAssets)[0].ref).toBe(assetRefValue2);
  });


});
