const assetUrl = (id) => `http://img.com/asset${id}.jpg`;
const assetFile = (id, folder = "folder") => `file://${folder}/asset${id}.jpg`;

const asset = (id, folder = "folder") => ({
  url: assetUrl(id),
  file: assetFile(id, folder),
});

const layoutId = "A1234";
const cachedFiles = [asset(1), asset(3), asset(5)];

const assetFiles = [asset(1), asset(2), asset(3), asset(4)];

const {
  localStorage: storage,
} = require("@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage");

const storageGetSpy = jest.spyOn(storage, "getItem");
const storageSetSpy = jest.spyOn(storage, "setItem");

storageGetSpy.mockImplementation(() =>
  Promise.resolve({
    [layoutId]: {
      id: layoutId,
      files: cachedFiles,
    },
  })
);

storageSetSpy.mockImplementation(jest.fn(() => Promise.resolve(true)));

const reactUtils = require("@applicaster/zapp-react-native-utils/reactUtils");
const platformSpy = jest.spyOn(reactUtils, "isTV");

const filesManager = require("@applicaster/zapp-react-native-bridge/OfflineAssets");

const filesManagerSpy = jest
  .spyOn(filesManager, "deleteFolders")
  .mockImplementation(jest.fn());

const { CacheManager } = require("../CacheManager");

function clearMocks(isTVFlag = false) {
  storageSetSpy.mockClear();
  storageGetSpy.mockClear();
  platformSpy.mockClear();
  platformSpy.mockImplementation(() => isTVFlag);
  filesManagerSpy.mockClear();
}

describe("CacheManager", () => {
  it("is a singleton", () => {
    expect(CacheManager.instance instanceof CacheManager).toBe(true);
  });

  describe("when running on TV", () => {
    beforeEach(() => {
      clearMocks(true);
    });

    it("returns empty files to cache", async () => {
      await expect(
        CacheManager.instance.getFilesToCache(layoutId, assetFiles)
      ).resolves.toEqual(
        expect.objectContaining({
          removedFiles: [],
          newFiles: [],
        })
      );
    });

    it("doesn't call localStorage", async () => {
      await CacheManager.instance.getFilesToCache(layoutId, assetFiles);
      expect(storageGetSpy).not.toHaveBeenCalled();
    });
  });

  describe("when loading a new layout", () => {
    beforeEach(() => clearMocks());

    it("returns all the files to cache", async () => {
      await expect(
        CacheManager.instance.getFilesToCache("B4567", assetFiles)
      ).resolves.toEqual(
        expect.objectContaining({
          removedFiles: [],
          newFiles: assetFiles,
        })
      );
    });
  });

  describe("when loading an existing layout", () => {
    beforeEach(() => clearMocks());

    it("returns the files to remove and to add", async () => {
      await expect(
        CacheManager.instance.getFilesToCache(layoutId, assetFiles)
      ).resolves.toEqual(
        expect.objectContaining({
          removedFiles: [asset(5)],
          newFiles: [asset(2), asset(4)],
        })
      );
    });
  });

  describe("clearCache", () => {
    beforeEach(() => clearMocks());

    it("removes the files from the cache", async () => {
      await CacheManager.instance.clearCache([
        asset(1),
        asset(2),
        asset(3, "path"),
      ]);

      expect(filesManagerSpy).toHaveBeenCalledWith([
        "file://folder/asset1.jpg",
        "file://folder/asset2.jpg",
        "file://path/asset3.jpg",
      ]);
    });
  });

  describe("saveCacheData", () => {
    beforeEach(() => clearMocks());

    it("saves the cache data", async () => {
      await CacheManager.instance.saveCacheData(layoutId, assetFiles);

      expect(storageSetSpy).toHaveBeenCalledWith(
        "APP_ASSETS_CACHE_DATA_KEY",
        expect.objectContaining({
          [layoutId]: {
            id: layoutId,
            files: assetFiles,
          },
        }),
        "offline_mode"
      );
    });
  });
});
